diff --git a/Apps/Sandcastle/CesiumSandcastle.js b/Apps/Sandcastle/CesiumSandcastle.js index f368ad929523..23101ff5285d 100644 --- a/Apps/Sandcastle/CesiumSandcastle.js +++ b/Apps/Sandcastle/CesiumSandcastle.js @@ -49,8 +49,7 @@ require({ 'Sandcastle/LinkButton', 'Source/Core/defined', 'Source/Core/getBaseUri', - 'Source/Core/loadJsonp', - 'Source/Core/loadWithXhr', + 'Source/Core/Resource', 'Source/Cesium', 'CodeMirror/addon/hint/show-hint', 'CodeMirror/addon/hint/javascript-hint', @@ -96,8 +95,7 @@ require({ LinkButton, defined, getBaseUri, - loadJsonp, - loadWithXhr, + Resource, Cesium ) { 'use strict'; @@ -747,7 +745,7 @@ require({ demoCode = scriptCode.replace(/\s/g, ''); if (defined(queryObject.gistId)) { - loadJsonp('https://api.github.com/gists/' + queryObject.gistId + '?access_token=dd8f755c2e5d9bbb26806bb93eaa2291f2047c60') + Resource.fetchJsonp('https://api.github.com/gists/' + queryObject.gistId + '?access_token=dd8f755c2e5d9bbb26806bb93eaa2291f2047c60') .then(function(data) { var files = data.data.files; var code = files['Cesium-Sandcastle.js'].content; @@ -957,11 +955,9 @@ require({ } } }; - return loadWithXhr({ - url : 'https://api.github.com/gists', - data : JSON.stringify(data), - method : 'POST' - }).then(function(content) { + + var resource = new Resource('https://api.github.com/gists'); + return resource.post(JSON.stringify(data)).then(function(content) { sandcastleUrl = getBaseUri(window.location.href) + '?src=Hello%20World.html&label=Showcases&gist=' + JSON.parse(content).id; textArea.value = sandcastleUrl; textArea.select(); diff --git a/Apps/Sandcastle/gallery/Billboards.html b/Apps/Sandcastle/gallery/Billboards.html index 6b404b457616..a9bcac7daed5 100644 --- a/Apps/Sandcastle/gallery/Billboards.html +++ b/Apps/Sandcastle/gallery/Billboards.html @@ -144,8 +144,8 @@ function offsetByDistance() { Sandcastle.declare(offsetByDistance); Cesium.when.all([ - Cesium.loadImage('../images/Cesium_Logo_overlay.png'), - Cesium.loadImage('../images/facility.gif') + Cesium.Resource.fetchImage('../images/Cesium_Logo_overlay.png'), + Cesium.Resource.fetchImage('../images/facility.gif') ], function(images) { // As viewer zooms closer to facility billboard, diff --git a/Apps/Sandcastle/gallery/Custom DataSource.html b/Apps/Sandcastle/gallery/Custom DataSource.html index d2c85eaf5ddc..35cc031d4971 100644 --- a/Apps/Sandcastle/gallery/Custom DataSource.html +++ b/Apps/Sandcastle/gallery/Custom DataSource.html @@ -238,7 +238,7 @@ //Use 'when' to load the URL into a json object //and then process is with the `load` function. var that = this; - return Cesium.when(Cesium.loadJson(url), function(json) { + return Cesium.Resource.fetchJson(url).then(function(json) { return that.load(json, url); }).otherwise(function(error) { //Otherwise will catch any errors or exceptions that occur diff --git a/Apps/Sandcastle/gallery/Custom Geocoder.html b/Apps/Sandcastle/gallery/Custom Geocoder.html index ac8bf7ef022a..2d3699b6626d 100644 --- a/Apps/Sandcastle/gallery/Custom Geocoder.html +++ b/Apps/Sandcastle/gallery/Custom Geocoder.html @@ -51,10 +51,16 @@ * @returns {Promise} */ OpenStreetMapNominatimGeocoder.prototype.geocode = function (input) { - var endpoint = 'https://nominatim.openstreetmap.org/search?'; - var query = 'format=json&q=' + input; - var requestString = endpoint + query; - return Cesium.loadJson(requestString) + var endpoint = 'https://nominatim.openstreetmap.org/search'; + var resource = new Cesium.Resource({ + url: endpoint, + queryParameters: { + format: 'json', + q: input + } + }); + + return resource.fetchJson() .then(function (results) { var bboxDegrees; return results.map(function (resultObject) { diff --git a/Apps/Sandcastle/gallery/Reverse Geocoder.html b/Apps/Sandcastle/gallery/Reverse Geocoder.html index 6188f1b17f7a..36164ee0087b 100644 --- a/Apps/Sandcastle/gallery/Reverse Geocoder.html +++ b/Apps/Sandcastle/gallery/Reverse Geocoder.html @@ -73,12 +73,13 @@ var longitudeDegrees = Cesium.Math.toDegrees(cartographic.longitude); var latitudeDegrees = Cesium.Math.toDegrees(cartographic.latitude); var url = baseUrl + latitudeDegrees + "," +longitudeDegrees; - var promise = Cesium.loadJsonp(url, { - parameters : { + var resource = new Cesium.Resource({ + url: url, + queryParameters: { key : Cesium.BingMapsApi.getKey() - }, - callbackParameterName : 'jsonp' + } }); + var promise = resource.fetchJsonp('jsonp'); promise.then(function(result) { var resources = result.resourceSets[0].resources; diff --git a/Apps/Sandcastle/gallery/development/Billboards.html b/Apps/Sandcastle/gallery/development/Billboards.html index 1a551de8a35e..6c85e49555ea 100644 --- a/Apps/Sandcastle/gallery/development/Billboards.html +++ b/Apps/Sandcastle/gallery/development/Billboards.html @@ -141,8 +141,8 @@ function offsetByDistance() { Sandcastle.declare(offsetByDistance); Cesium.when.all([ - Cesium.loadImage('../images/Cesium_Logo_overlay.png'), - Cesium.loadImage('../images/facility.gif') + Cesium.Resource.createIfNeeded('../images/Cesium_Logo_overlay.png').fetchImage(), + Cesium.Resource.createIfNeeded('../images/facility.gif').fetchImage() ], function(images) { var billboards = scene.primitives.add(new Cesium.BillboardCollection()); diff --git a/CHANGES.md b/CHANGES.md index 0e93c3edb779..cc903dbad053 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,8 +7,9 @@ Change Log * The clock does not animate by default. Set the `shouldAnimate` option to `true` when creating the Viewer to enable animation. * Deprecated * For all classes/functions that can now take a `Resource` instance, all additional parameters that are part of the `Resource` class have been deprecated and will be removed in Cesium 1.44. This generally includes `proxy`, `headers` and `query` parameters. + * All low level load functions including `loadArrayBuffer`, `loadBlob`, `loadImage`, `loadJson`, `loadJsonp`, `loadText`, `loadXML` and `loadWithXhr` have been deprecated and will be removed in Cesium 1.44. Please use the equivalent `fetch` functions on the `Resource` class. * Fixed Sandcastle for IE 11. [#6169](https://github.com/AnalyticalGraphicsInc/cesium/pull/6169) -* Major refactor of URL handling. All classes that take a url parameter, can now take a Resource or a String. This includes all imagery providers, all terrain providers, `Cesium3DTileset`, `KMLDataSource`, `CZMLDataSource`, `GeoJsonDataSource`, `Model`, `Billboard`, along with all the low level `load*()` functions. +* Major refactor of URL handling. All classes that take a url parameter, can now take a Resource or a String. This includes all imagery providers, all terrain providers, `Cesium3DTileset`, `KMLDataSource`, `CZMLDataSource`, `GeoJsonDataSource`, `Model`, and `Billboard`. * Added `ClippingPlaneCollection.isSupported` function for checking if rendering with clipping planes is supported. * Added new `CesiumIon` utility class for working with the Cesium ion beta API. * Improved CZML Custom Properties sandcastle example [#6086](https://github.com/AnalyticalGraphicsInc/cesium/pull/6086) @@ -33,7 +34,7 @@ Change Log * Updated documentation links to reflect new locations on cesiumjs.org and cesium.com. * Updated 'Viewer.zoomTo' and 'Viewer.flyTo' to take in Cesium3DTilesets as a target and updated sandcastle 3DTileset examples to reflect this change * Added optional scene request render mode to reduce CPU usage. [#6065](https://github.com/AnalyticalGraphicsInc/cesium/pull/6065) and [#6107](https://github.com/AnalyticalGraphicsInc/cesium/pull/6107) - * `Scene.requestRenderMode` enables a mode which will only request new render frames on changes to the scene, or when the simulation time change exceeds `scene.maximumRenderTimeChange`. + * `Scene.requestRenderMode` enables a mode which will only request new render frames on changes to the scene, or when the simulation time change exceeds `scene.maximumRenderTimeChange`. * `Scene.requestRender` will explicitly request a new render frame when in request render mode. * Added `Scene.preUpdate` and `Scene.postUpdate` events that are raised before and after the scene updates respectively. The scene is always updated before executing a potential render. Continue to listen to `Scene.preRender and `Scene.postRender` events for when the scene renders a frame. * Added `CreditDisplay.update`, which updates the credit display before a new frame is rendered. diff --git a/Source/Core/BingMapsGeocoderService.js b/Source/Core/BingMapsGeocoderService.js index 702053a628b7..781f060e4656 100644 --- a/Source/Core/BingMapsGeocoderService.js +++ b/Source/Core/BingMapsGeocoderService.js @@ -4,7 +4,6 @@ define([ './defaultValue', './defined', './defineProperties', - './loadJsonp', './Rectangle', './Resource' ], function( @@ -13,7 +12,6 @@ define([ defaultValue, defined, defineProperties, - loadJsonp, Rectangle, Resource) { 'use strict'; @@ -96,7 +94,7 @@ define([ } }); - return loadJsonp(resource, 'jsonp').then(function(result) { + return resource.fetchJsonp('jsonp').then(function(result) { if (result.resourceSets.length === 0) { return []; } diff --git a/Source/Core/CesiumTerrainProvider.js b/Source/Core/CesiumTerrainProvider.js index 8c71d940a127..613c0b95e708 100644 --- a/Source/Core/CesiumTerrainProvider.js +++ b/Source/Core/CesiumTerrainProvider.js @@ -14,8 +14,6 @@ define([ './GeographicTilingScheme', './HeightmapTerrainData', './IndexDatatype', - './loadArrayBuffer', - './loadJson', './Math', './OrientedBoundingBox', './QuantizedMeshTerrainData', @@ -40,8 +38,6 @@ define([ GeographicTilingScheme, HeightmapTerrainData, IndexDatatype, - loadArrayBuffer, - loadJson, CesiumMath, OrientedBoundingBox, QuantizedMeshTerrainData, @@ -289,7 +285,7 @@ define([ metadataResource = lastResource.getDerivedResource({ url: 'layer.json' }); - var parentMetadata = loadJson(metadataResource); + var parentMetadata = metadataResource.fetchJson(); return when(parentMetadata, parseMetadataSuccess, parseMetadataFailure); } @@ -347,7 +343,7 @@ define([ } function requestMetadata() { - var metadata = loadJson(metadataResource); + var metadata = metadataResource.fetchJson(); when(metadata, metadataSuccess, metadataFailure); } @@ -623,7 +619,7 @@ define([ request: request }); - var promise = loadArrayBuffer(resource); + var promise = resource.fetchArrayBuffer(); if (!defined(promise)) { return undefined; diff --git a/Source/Core/EarthOrientationParameters.js b/Source/Core/EarthOrientationParameters.js index 6252631e9172..d24a7670ba3d 100644 --- a/Source/Core/EarthOrientationParameters.js +++ b/Source/Core/EarthOrientationParameters.js @@ -7,7 +7,6 @@ define([ './freezeObject', './JulianDate', './LeapSecond', - './loadJson', './Resource', './RuntimeError', './TimeConstants', @@ -21,7 +20,6 @@ define([ freezeObject, JulianDate, LeapSecond, - loadJson, Resource, RuntimeError, TimeConstants, @@ -99,7 +97,7 @@ define([ // Download EOP data. var that = this; - this._downloadPromise = when(loadJson(resource), function(eopData) { + this._downloadPromise = when(resource.fetchJson(), function(eopData) { onDataReady(that, eopData); }, function() { that._dataError = 'An error occurred while retrieving the EOP data from the URL ' + resource.url + '.'; diff --git a/Source/Core/GoogleEarthEnterpriseMetadata.js b/Source/Core/GoogleEarthEnterpriseMetadata.js index 6211736d02b0..1ad3392796f7 100644 --- a/Source/Core/GoogleEarthEnterpriseMetadata.js +++ b/Source/Core/GoogleEarthEnterpriseMetadata.js @@ -10,7 +10,6 @@ define([ './deprecationWarning', './GoogleEarthEnterpriseTileInformation', './isBitSet', - './loadArrayBuffer', './Math', './Request', './Resource', @@ -28,7 +27,6 @@ define([ deprecationWarning, GoogleEarthEnterpriseTileInformation, isBitSet, - loadArrayBuffer, CesiumMath, Request, Resource, @@ -326,7 +324,7 @@ define([ quadKey = defaultValue(quadKey, ''); var resource = getMetadataResource(this, quadKey, version, request); - var promise = loadArrayBuffer(resource); + var promise = resource.fetchArrayBuffer(); if (!defined(promise)) { return undefined; // Throttled @@ -509,7 +507,7 @@ define([ } }); - return loadArrayBuffer(resource) + return resource.fetchArrayBuffer() .then(function(buf) { var encryptedDbRootProto = dbrootParser.EncryptedDbRootProto.decode(new Uint8Array(buf)); diff --git a/Source/Core/GoogleEarthEnterpriseTerrainProvider.js b/Source/Core/GoogleEarthEnterpriseTerrainProvider.js index 76ba71fddcb9..bb0cc9f0e299 100644 --- a/Source/Core/GoogleEarthEnterpriseTerrainProvider.js +++ b/Source/Core/GoogleEarthEnterpriseTerrainProvider.js @@ -12,7 +12,6 @@ define([ './GoogleEarthEnterpriseTerrainData', './HeightmapTerrainData', './JulianDate', - './loadArrayBuffer', './Math', './Rectangle', './Request', @@ -36,7 +35,6 @@ define([ GoogleEarthEnterpriseTerrainData, HeightmapTerrainData, JulianDate, - loadArrayBuffer, CesiumMath, Rectangle, Request, @@ -450,7 +448,7 @@ define([ sharedRequest = terrainRequests[q]; } else { // Create new request for terrain sharedRequest = request; - var requestPromise = loadArrayBuffer(buildTerrainResource(this, q, terrainVersion, sharedRequest)); + var requestPromise = buildTerrainResource(this, q, terrainVersion, sharedRequest).fetchArrayBuffer(); if (!defined(requestPromise)) { return undefined; // Throttled diff --git a/Source/Core/Iau2006XysData.js b/Source/Core/Iau2006XysData.js index 44a9450d62a7..1cebba46737b 100644 --- a/Source/Core/Iau2006XysData.js +++ b/Source/Core/Iau2006XysData.js @@ -5,7 +5,6 @@ define([ './defined', './Iau2006XysSample', './JulianDate', - './loadJson', './Resource', './TimeStandard' ], function( @@ -15,7 +14,6 @@ define([ defined, Iau2006XysSample, JulianDate, - loadJson, Resource, TimeStandard) { 'use strict'; @@ -246,10 +244,12 @@ define([ } }); } else { - chunkUrl = buildModuleUrl('Assets/IAU2006_XYS/IAU2006_XYS_' + chunkIndex + '.json'); + chunkUrl = new Resource({ + url : buildModuleUrl('Assets/IAU2006_XYS/IAU2006_XYS_' + chunkIndex + '.json') + }); } - when(loadJson(chunkUrl), function(chunk) { + when(chunkUrl.fetchJson(), function(chunk) { xysData._chunkDownloadsInProgress[chunkIndex] = false; var samples = xysData._samples; diff --git a/Source/Core/PinBuilder.js b/Source/Core/PinBuilder.js index c35bd467bb07..2e1cddd3058d 100644 --- a/Source/Core/PinBuilder.js +++ b/Source/Core/PinBuilder.js @@ -3,7 +3,6 @@ define([ './Color', './defined', './DeveloperError', - './loadImage', './Resource', './writeTextToCanvas' ], function( @@ -11,7 +10,6 @@ define([ Color, defined, DeveloperError, - loadImage, Resource, writeTextToCanvas) { 'use strict'; @@ -218,7 +216,7 @@ define([ var resource = Resource.createIfNeeded(url); //If we have an image url, load it and then stamp the pin. - var promise = loadImage(resource).then(function(image) { + var promise = resource.fetchImage().then(function(image) { drawIcon(context2D, image, size); cache[id] = canvas; return canvas; diff --git a/Source/Core/Request.js b/Source/Core/Request.js index 154ad387de17..c299876b23da 100644 --- a/Source/Core/Request.js +++ b/Source/Core/Request.js @@ -1,9 +1,11 @@ define([ './defaultValue', + './defined', './RequestState', './RequestType' ], function( defaultValue, + defined, RequestState, RequestType) { 'use strict'; @@ -148,6 +150,37 @@ define([ this.cancelled = true; }; + + /** + * Duplicates a Request instance. + * + * @param {Request} [result] The object onto which to store the result. + * + * @returns {Request} The modified result parameter or a new Resource instance if one was not provided. + */ + Request.prototype.clone = function(result) { + if (!defined(result)) { + return new Request(this); + } + + result.url = this.url; + result.requestFunction = this.requestFunction; + result.cancelFunction = this.cancelFunction; + result.priorityFunction = this.priorityFunction; + result.priority = this.priority; + result.throttle = this.throttle; + result.throttleByServer = this.throttleByServer; + result.type = this.type; + result.serverKey = this.serverKey; + + // These get defaulted because the cloned request hasn't been issued + result.state = this.RequestState.UNISSUED; + result.deferred = undefined; + result.cancelled = false; + + return result; + }; + /** * The function that makes the actual data request. * @callback Request~RequestCallback diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 355fd10eece0..6eb98c9914a2 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -6,13 +6,23 @@ define([ './defaultValue', './defined', './defineProperties', + './deprecationWarning', + './DeveloperError', './freezeObject', './getAbsoluteUri', './getBaseUri', './getExtensionFromUri', + './isBlobUri', + './isCrossOriginUrl', './isDataUri', './objectToQuery', './queryToObject', + './Request', + './RequestErrorEvent', + './RequestScheduler', + './RequestState', + './RuntimeError', + './TrustedServers', '../ThirdParty/Uri', '../ThirdParty/when' ], function(appendForwardSlash, @@ -22,17 +32,38 @@ define([ defaultValue, defined, defineProperties, + deprecationWarning, + DeveloperError, freezeObject, getAbsoluteUri, getBaseUri, getExtensionFromUri, + isBlobUri, + isCrossOriginUrl, isDataUri, objectToQuery, queryToObject, + Request, + RequestErrorEvent, + RequestScheduler, + RequestState, + RuntimeError, + TrustedServers, Uri, when) { 'use strict'; + var xhrBlobSupported = (function() { + try { + var xhr = new XMLHttpRequest(); + xhr.open('GET', '#', true); + xhr.responseType = 'blob'; + return xhr.responseType === 'blob'; + } catch (e) { + return false; + } + })(); + /** * @private */ @@ -83,18 +114,29 @@ define([ return defined(obj.clone) ? obj.clone() : clone(obj); } + /** + * @private + */ + function checkAndResetRequest(request) { + if (request.state === RequestState.ISSUED || request.state === RequestState.ACTIVE) { + throw new RuntimeError('The Resource is already being fetched.'); + } + + request.state = RequestState.UNISSUED; + request.deferred = undefined; + } + /** * A resource that includes the location and any other parameters we need to retrieve it or create derived resources. It also provides the ability to retry requests. * - * @param {Object} options An object with the following properties + * @alias Resource + * @constructor + * + * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. - * @param {String} [options.responseType] The type of response. - * @param {String} [options.method='GET'] The method to use. - * @param {Object} [options.data] Data that is sent with the resource request if method is PUT or POST. - * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. @@ -129,11 +171,14 @@ define([ * retryCallback: refreshTokenRetryCallback, * retryAttempts: 1 * }); - * - * @constructor */ function Resource(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); + if (typeof options === 'string') { + options = { + url: options + }; + } //>>includeStart('debug', pragmas.debug); Check.typeOf.string('options.url', options.url); @@ -155,35 +200,7 @@ define([ * * @type {Request} */ - this.request = options.request; - - /** - * The type of response expected from the request. - * - * @type {String} - */ - this.responseType = options.responseType; - - /** - * The method to use for the request. - * - * @type {String} - */ - this.method = defaultValue(options.method, 'GET'); - - /** - * Data to be sent with the request. - * - * @type {Object} - */ - this.data = options.data; - - /** - * Overrides the MIME type returned by the server. - * - * @type {String} - */ - this.overrideMimeType = options.overrideMimeType; + this.request = defaultValue(options.request, new Request()); /** * A proxy to be used when loading the resource. @@ -234,6 +251,22 @@ define([ return new Resource(args); }; + defineProperties(Resource, { + /** + * Returns true if blobs are supported. + * + * @memberof Resource + * @type {Boolean} + * + * @readonly + */ + isBlobSupported : { + get : function() { + return xhrBlobSupported; + } + } + }); + defineProperties(Resource.prototype, { /** * Query parameters appended to the url. @@ -309,6 +342,42 @@ define([ get: function() { return isDataUri(this._url); } + }, + + /** + * True if the Resource refers to a blob URI. + * + * @memberof Resource.prototype + * @type {Boolean} + */ + isBlobUri: { + get: function() { + return isBlobUri(this._url); + } + }, + + /** + * True if the Resource refers to a cross origin URL. + * + * @memberof Resource.prototype + * @type {Boolean} + */ + isCrossOriginUrl: { + get: function() { + return isCrossOriginUrl(this._url); + } + }, + + /** + * True if the Resource has request headers. This is equivalent to checking if the headers property has any keys. + * + * @memberof Resource.prototype + * @type {Boolean} + */ + hasHeaders: { + get: function() { + return (Object.keys(this.headers).length > 0); + } } }); @@ -387,10 +456,6 @@ define([ * @param {Object} [options.queryParameters] An object containing query parameters that will be combined with those of the current instance. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). These will be combined with those of the current instance. * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. - * @param {String} [options.responseType] The type of response. - * @param {String} [options.method] The method to use. - * @param {Object} [options.data] Data that is sent with the resource request if method is PUT or POST. - * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource~RetryCallback} [options.retryCallback] The function to call when loading the resource fails. * @param {Number} [options.retryAttempts] The number of times the retryCallback should be called before giving up. @@ -422,23 +487,14 @@ define([ if (defined(options.headers)) { resource.headers = combine(options.headers, resource.headers); } - if (defined(options.responseType)) { - resource.responseType = options.responseType; - } - if (defined(options.method)) { - resource.method = options.method; - } - if (defined(options.data)) { - resource.data = options.data; - } - if (defined(options.overrideMimeType)) { - resource.overrideMimeType = options.overrideMimeType; - } if (defined(options.proxy)) { resource.proxy = options.proxy; } if (defined(options.request)) { resource.request = options.request; + } else { + // Clone the request so we keep all the throttle settings + resource.request = this.request.clone(); } if (defined(options.retryCallback)) { resource.retryCallback = options.retryCallback; @@ -490,10 +546,6 @@ define([ result._queryParameters = clone(this._queryParameters); result._templateValues = clone(this._templateValues); result.headers = clone(this.headers); - result.responseType = this.responseType; - result.method = this.method; - result.data = this.data; - result.overrideMimeType = this.overrideMimeType; result.proxy = this.proxy; result.retryCallback = this.retryCallback; result.retryAttempts = this.retryAttempts; @@ -524,6 +576,859 @@ define([ this._url = appendForwardSlash(this._url); }; + /** + * Asynchronously loads the resource as raw binary data. Returns a promise that will resolve to + * an ArrayBuffer once loaded, or reject if the resource failed to load. The data is loaded + * using XMLHttpRequest, which means that in order to make requests to another origin, + * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. + * + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * @example + * // load a single URL asynchronously + * resource.fetchArrayBuffer().then(function(arrayBuffer) { + * // use the data + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetchArrayBuffer = function () { + return this.fetch({ + responseType : 'arraybuffer' + }); + }; + + /** + * Creates a Resource and calls fetchArrayBuffer() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetchArrayBuffer = function (options) { + var resource = new Resource(options); + return resource.fetchArrayBuffer(); + }; + + /** + * Asynchronously loads the given resource as a blob. Returns a promise that will resolve to + * a Blob once loaded, or reject if the resource failed to load. The data is loaded + * using XMLHttpRequest, which means that in order to make requests to another origin, + * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. + * + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * @example + * // load a single URL asynchronously + * resource.fetchBlob().then(function(blob) { + * // use the data + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetchBlob = function () { + return this.fetch({ + responseType : 'blob' + }); + }; + + /** + * Creates a Resource and calls fetchBlob() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetchBlob = function (options) { + var resource = new Resource(options); + return resource.fetchBlob(); + }; + + /** + * Asynchronously loads the given image resource. Returns a promise that will resolve to + * an {@link Image} once loaded, or reject if the image failed to load. + * + * @param {Boolean} [preferBlob = false] If true, we will load the image via a blob. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * + * @example + * // load a single image asynchronously + * resource.fetchImage().then(function(image) { + * // use the loaded image + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * // load several images in parallel + * when.all([resource1.fetchImage(), resource2.fetchImage()]).then(function(images) { + * // images is an array containing all the loaded images + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetchImage = function (preferBlob, allowCrossOrigin) { + if (defined(allowCrossOrigin)) { + deprecationWarning('Resource.fetchImage.allowCrossOrigin', 'The allowCrossOrigin parameter has been deprecated and will be removed in Cesium 1.44. It no longer needs to be specified.'); + } + + preferBlob = defaultValue(preferBlob, false); + allowCrossOrigin = defaultValue(allowCrossOrigin, true); + + checkAndResetRequest(this.request); + + // We try to load the image normally if + // 1. Blobs aren't supported + // 2. It's a data URI + // 3. It's a blob URI + // 4. It doesn't have request headers and we preferBlob is false + if (!xhrBlobSupported || this.isDataUri || this.isBlobUri || (!this.hasHeaders && !preferBlob)) { + return fetchImage(this, allowCrossOrigin); + } + + var blobPromise = this.fetchBlob(); + if (!defined(blobPromise)) { + return; + } + + var generatedBlobResource; + var generatedBlob; + return blobPromise + .then(function(blob) { + if (!defined(blob)) { + return; + } + generatedBlob = blob; + var blobUrl = window.URL.createObjectURL(blob); + generatedBlobResource = new Resource({ + url: blobUrl + }); + + return fetchImage(generatedBlobResource); + }) + .then(function(image) { + if (!defined(image)) { + return; + } + window.URL.revokeObjectURL(generatedBlobResource.url); + + // This is because the blob object is needed for DiscardMissingTileImagePolicy + // See https://github.com/AnalyticalGraphicsInc/cesium/issues/1353 + image.blob = generatedBlob; + return image; + }) + .otherwise(function(error) { + if (defined(generatedBlobResource)) { + window.URL.revokeObjectURL(generatedBlobResource.url); + } + + return when.reject(error); + }); + }; + + function fetchImage(resource, allowCrossOrigin) { + var request = resource.request; + request.url = resource.url; + request.requestFunction = function() { + var url = resource.url; + var crossOrigin = false; + + // data URIs can't have allowCrossOrigin set. + if (!resource.isDataUri && !resource.isBlobUri) { + crossOrigin = resource.isCrossOriginUrl; + } + + var deferred = when.defer(); + + Resource._Implementations.createImage(url, crossOrigin && allowCrossOrigin, deferred); + + return deferred.promise; + }; + + var promise = RequestScheduler.request(request); + if (!defined(promise)) { + return; + } + + return promise + .otherwise(function(e) { + // Don't retry cancelled or otherwise aborted requests + if (request.state !== RequestState.FAILED) { + return when.reject(e); + } + + return resource.retryOnError(e) + .then(function(retry) { + if (retry) { + // Reset request so it can try again + request.state = RequestState.UNISSUED; + request.deferred = undefined; + + return fetchImage(resource, allowCrossOrigin); + } + + return when.reject(e); + }); + }); + } + + /** + * Creates a Resource and calls fetchImage() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @param {Boolean} [options.preferBlob = false] If true, we will load the image via a blob. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetchImage = function (options) { + var resource = new Resource(options); + return resource.fetchImage(options.preferBlob, options.allowCrossOrigin); + }; + + /** + * Asynchronously loads the given resource as text. Returns a promise that will resolve to + * a String once loaded, or reject if the resource failed to load. The data is loaded + * using XMLHttpRequest, which means that in order to make requests to another origin, + * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. + * + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * @example + * // load text from a URL, setting a custom header + * var resource = new Resource({ + * url: 'http://someUrl.com/someJson.txt', + * headers: { + * 'X-Custom-Header' : 'some value' + * } + * }); + * resource.fetchText().then(function(text) { + * // Do something with the text + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetchText = function() { + return this.fetch({ + responseType : 'text' + }); + }; + + /** + * Creates a Resource and calls fetchText() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetchText = function (options) { + var resource = new Resource(options); + return resource.fetchText(); + }; + + // note: */* below is */* but that ends the comment block early + /** + * Asynchronously loads the given resource as JSON. Returns a promise that will resolve to + * a JSON object once loaded, or reject if the resource failed to load. The data is loaded + * using XMLHttpRequest, which means that in order to make requests to another origin, + * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. This function + * adds 'Accept: application/json,*/*;q=0.01' to the request headers, if not + * already specified. + * + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * + * @example + * resource.fetchJson().then(function(jsonData) { + * // Do something with the JSON object + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetchJson = function() { + var promise = this.fetch({ + responseType : 'text', + headers: { + Accept : 'application/json,*/*;q=0.01' + } + }); + + if (!defined(promise)) { + return undefined; + } + + return promise + .then(function(value) { + if (!defined(value)) { + return; + } + return JSON.parse(value); + }); + }; + + /** + * Creates a Resource and calls fetchJson() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetchJson = function (options) { + var resource = new Resource(options); + return resource.fetchJson(); + }; + + /** + * Asynchronously loads the given resource as XML. Returns a promise that will resolve to + * an XML Document once loaded, or reject if the resource failed to load. The data is loaded + * using XMLHttpRequest, which means that in order to make requests to another origin, + * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. + * + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * + * @example + * // load XML from a URL, setting a custom header + * Cesium.loadXML('http://someUrl.com/someXML.xml', { + * 'X-Custom-Header' : 'some value' + * }).then(function(document) { + * // Do something with the document + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetchXML = function() { + return this.fetch({ + responseType : 'document', + overrideMimeType : 'text/xml' + }); + }; + + /** + * Creates a Resource and calls fetchXML() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetchXML = function (options) { + var resource = new Resource(options); + return resource.fetchXML(); + }; + + /** + * Requests a resource using JSONP. + * + * @param {String} [callbackParameterName='callback'] The callback parameter name that the server expects. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * + * @example + * // load a data asynchronously + * resource.loadJsonp().then(function(data) { + * // use the loaded data + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetchJsonp = function(callbackParameterName) { + callbackParameterName = defaultValue(callbackParameterName, 'callback'); + + checkAndResetRequest(this.request); + + //generate a unique function name + var functionName; + do { + functionName = 'loadJsonp' + Math.random().toString().substring(2, 8); + } while (defined(window[functionName])); + + return fetchJsonp(this, callbackParameterName, functionName); + }; + + function fetchJsonp(resource, callbackParameterName, functionName) { + var callbackQuery = {}; + callbackQuery[callbackParameterName] = functionName; + resource.addQueryParameters(callbackQuery); + + var request = resource.request; + request.url = resource.url; + request.requestFunction = function() { + var deferred = when.defer(); + + //assign a function with that name in the global scope + window[functionName] = function(data) { + deferred.resolve(data); + + try { + delete window[functionName]; + } catch (e) { + window[functionName] = undefined; + } + }; + + Resource._Implementations.loadAndExecuteScript(resource.url, functionName, deferred); + return deferred.promise; + }; + + var promise = RequestScheduler.request(request); + if (!defined(promise)) { + return; + } + + return promise + .otherwise(function(e) { + if (request.state !== RequestState.FAILED) { + return when.reject(e); + } + + return resource.retryOnError(e) + .then(function(retry) { + if (retry) { + // Reset request so it can try again + request.state = RequestState.UNISSUED; + request.deferred = undefined; + + return fetchJsonp(resource, callbackParameterName, functionName); + } + + return when.reject(e); + }); + }); + } + + /** + * Creates a Resource from a URL and calls fetchJsonp() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @param {String} [options.callbackParameterName='callback'] The callback parameter name that the server expects. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetchJsonp = function (options) { + var resource = new Resource(options); + return resource.fetchJsonp(options.callbackParameterName); + }; + + /** + * Asynchronously loads the given resource. Returns a promise that will resolve to + * the result once loaded, or reject if the resource failed to load. The data is loaded + * using XMLHttpRequest, which means that in order to make requests to another origin, + * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. + * + * @param {Object} [options] Object with the following properties: + * @param {String} [options.responseType] The type of response. This controls the type of item returned. + * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. + * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * + * @example + * // Load a single resource asynchronously. In real code, you should use loadBlob instead. + * resource.fetch() + * .then(function(blob) { + * // use the data + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.fetch = function(options) { + options = defaultClone(options, defaultValue.EMPTY_OBJECT); + options.method = 'GET'; + + return makeRequest(this, options); + }; + + function makeRequest(resource, options) { + checkAndResetRequest(resource.request); + + var request = resource.request; + request.url = resource.url; + + request.requestFunction = function() { + var responseType = options.responseType; + var headers = combine(resource.headers, options.headers); + var overrideMimeType = options.overrideMimeType; + var method = options.method; + var data = options.data; + var deferred = when.defer(); + var xhr = Resource._Implementations.loadWithXhr(resource.url, responseType, method, data, headers, deferred, overrideMimeType); + if (defined(xhr) && defined(xhr.abort)) { + request.cancelFunction = function() { + xhr.abort(); + }; + } + return deferred.promise; + }; + + var promise = RequestScheduler.request(request); + if (!defined(promise)) { + return; + } + + return promise + .then(function(data) { + return data; + }) + .otherwise(function(e) { + if (request.state !== RequestState.FAILED) { + return when.reject(e); + } + + return resource.retryOnError(e) + .then(function(retry) { + if (retry) { + // Reset request so it can try again + request.state = RequestState.UNISSUED; + request.deferred = undefined; + + return resource.fetch(options); + } + + return when.reject(e); + }); + }); + } + + var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; + + function decodeDataUriText(isBase64, data) { + var result = decodeURIComponent(data); + if (isBase64) { + return atob(result); + } + return result; + } + + function decodeDataUriArrayBuffer(isBase64, data) { + var byteString = decodeDataUriText(isBase64, data); + var buffer = new ArrayBuffer(byteString.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < byteString.length; i++) { + view[i] = byteString.charCodeAt(i); + } + return buffer; + } + + function decodeDataUri(dataUriRegexResult, responseType) { + responseType = defaultValue(responseType, ''); + var mimeType = dataUriRegexResult[1]; + var isBase64 = !!dataUriRegexResult[2]; + var data = dataUriRegexResult[3]; + + switch (responseType) { + case '': + case 'text': + return decodeDataUriText(isBase64, data); + case 'arraybuffer': + return decodeDataUriArrayBuffer(isBase64, data); + case 'blob': + var buffer = decodeDataUriArrayBuffer(isBase64, data); + return new Blob([buffer], { + type : mimeType + }); + case 'document': + var parser = new DOMParser(); + return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType); + case 'json': + return JSON.parse(decodeDataUriText(isBase64, data)); + default: + //>>includeStart('debug', pragmas.debug); + throw new DeveloperError('Unhandled responseType: ' + responseType); + //>>includeEnd('debug'); + } + } + + /** + * Creates a Resource from a URL and calls fetch() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @param {String} [options.responseType] The type of response. This controls the type of item returned. + * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.fetch = function (options) { + var resource = new Resource(options); + return resource.fetch({ + // Make copy of just the needed fields because headers can be passed to both the constructor and to fetch + responseType: options.responseType, + overrideMimeType: options.overrideMimeType + }); + }; + + /** + * Asynchronously posts data the given resource. Returns a promise that will resolve to + * the result once loaded, or reject if the resource failed to load. The data is loaded + * using XMLHttpRequest, which means that in order to make requests to another origin, + * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. + * + * @param {Object} data Data that is posted with the resource. + * @param {Object} [options] Object with the following properties: + * @param {String} [options.responseType] The type of response. This controls the type of item returned. + * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. + * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * + * + * @example + * // Load a single resource asynchronously. In real code, you should use loadBlob instead. + * resource.post(data) + * .then(function(result) { + * // use the result + * }).otherwise(function(error) { + * // an error occurred + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + */ + Resource.prototype.post = function(data, options) { + Check.defined('data', data); + + options = defaultClone(options, {}); + options.method = 'POST'; + options.data = data; + + return makeRequest(this, options); + }; + + /** + * Creates a Resource from a URL and calls fetch() on it. + * + * @param {String|Object} options A url or an object with the following properties + * @param {String} options.url The url of the resource. + * @param {Object} options.data Data that is posted with the resource. + * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. + * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). + * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. + * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. + * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. + * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. + * @param {String} [options.responseType] The type of response. This controls the type of item returned. + * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + */ + Resource.post = function (options) { + var resource = new Resource(options); + return resource.post(options.data, { + // Make copy of just the needed fields because headers can be passed to both the constructor and to post + responseType: options.responseType, + overrideMimeType: options.overrideMimeType + }); + }; + + /** + * Contains implementations of functions that can be replaced for testing + * + * @private + */ + Resource._Implementations = {}; + + Resource._Implementations.createImage = function(url, crossOrigin, deferred) { + var image = new Image(); + + image.onload = function() { + deferred.resolve(image); + }; + + image.onerror = function(e) { + deferred.reject(e); + }; + + if (crossOrigin) { + if (TrustedServers.contains(url)) { + image.crossOrigin = 'use-credentials'; + } else { + image.crossOrigin = ''; + } + } + + image.src = url; + }; + + Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + var dataUriRegexResult = dataUriRegex.exec(url); + if (dataUriRegexResult !== null) { + deferred.resolve(decodeDataUri(dataUriRegexResult, responseType)); + return; + } + + var xhr = new XMLHttpRequest(); + + if (TrustedServers.contains(url)) { + xhr.withCredentials = true; + } + + if (defined(overrideMimeType) && defined(xhr.overrideMimeType)) { + xhr.overrideMimeType(overrideMimeType); + } + + xhr.open(method, url, true); + + if (defined(headers)) { + for (var key in headers) { + if (headers.hasOwnProperty(key)) { + xhr.setRequestHeader(key, headers[key]); + } + } + } + + if (defined(responseType)) { + xhr.responseType = responseType; + } + + // While non-standard, file protocol always returns a status of 0 on success + var localFile = false; + if (typeof url === 'string') { + localFile = url.indexOf('file://') === 0; + } + + xhr.onload = function() { + if ((xhr.status < 200 || xhr.status >= 300) && !(localFile && xhr.status === 0)) { + deferred.reject(new RequestErrorEvent(xhr.status, xhr.response, xhr.getAllResponseHeaders())); + return; + } + + var response = xhr.response; + var browserResponseType = xhr.responseType; + + //All modern browsers will go into either the first or second if block or last else block. + //Other code paths support older browsers that either do not support the supplied responseType + //or do not support the xhr.response property. + if (xhr.status === 204) { + // accept no content + deferred.resolve(); + } else if (defined(response) && (!defined(responseType) || (browserResponseType === responseType))) { + deferred.resolve(response); + } else if ((responseType === 'json') && typeof response === 'string') { + try { + deferred.resolve(JSON.parse(response)); + } catch (e) { + deferred.reject(e); + } + } else if ((browserResponseType === '' || browserResponseType === 'document') && defined(xhr.responseXML) && xhr.responseXML.hasChildNodes()) { + deferred.resolve(xhr.responseXML); + } else if ((browserResponseType === '' || browserResponseType === 'text') && defined(xhr.responseText)) { + deferred.resolve(xhr.responseText); + } else { + deferred.reject(new RuntimeError('Invalid XMLHttpRequest response type.')); + } + }; + + xhr.onerror = function(e) { + deferred.reject(new RequestErrorEvent()); + }; + + xhr.send(data); + + return xhr; + }; + + Resource._Implementations.loadAndExecuteScript = function(url, functionName, deferred) { + var script = document.createElement('script'); + script.async = true; + script.src = url; + + var head = document.getElementsByTagName('head')[0]; + script.onload = function() { + script.onload = undefined; + head.removeChild(script); + }; + script.onerror = function(e) { + deferred.reject(e); + }; + + head.appendChild(script); + }; + + /** + * The default implementations + * + * @private + */ + Resource._DefaultImplementations = {}; + Resource._DefaultImplementations.createImage = Resource._Implementations.createImage; + Resource._DefaultImplementations.loadWithXhr = Resource._Implementations.loadWithXhr; + Resource._DefaultImplementations.loadAndExecuteScript = Resource._Implementations.loadAndExecuteScript; /** * A resource instance initialized to the current browser location diff --git a/Source/Core/VRTheWorldTerrainProvider.js b/Source/Core/VRTheWorldTerrainProvider.js index ee1f541a8f2a..21570b0b5961 100644 --- a/Source/Core/VRTheWorldTerrainProvider.js +++ b/Source/Core/VRTheWorldTerrainProvider.js @@ -11,8 +11,6 @@ define([ './GeographicTilingScheme', './getImagePixels', './HeightmapTerrainData', - './loadImage', - './loadXML', './Math', './Rectangle', './Resource', @@ -31,8 +29,6 @@ define([ GeographicTilingScheme, getImagePixels, HeightmapTerrainData, - loadImage, - loadXML, CesiumMath, Rectangle, Resource, @@ -151,7 +147,7 @@ define([ } function requestMetadata() { - when(loadXML(that._resource), metadataSuccess, metadataFailure); + when(that._resource.fetchXML(), metadataSuccess, metadataFailure); } requestMetadata(); @@ -279,7 +275,7 @@ define([ }, request: request }); - var promise = loadImage(resource); + var promise = resource.fetchImage(); if (!defined(promise)) { return undefined; } diff --git a/Source/Core/loadArrayBuffer.js b/Source/Core/loadArrayBuffer.js index 6080e61da5e4..8bb93545014c 100644 --- a/Source/Core/loadArrayBuffer.js +++ b/Source/Core/loadArrayBuffer.js @@ -2,13 +2,11 @@ define([ './Check', './defined', './deprecationWarning', - './loadWithXhr', './Resource' ], function( Check, defined, deprecationWarning, - loadWithXhr, Resource) { 'use strict'; @@ -21,6 +19,8 @@ define([ * @exports loadArrayBuffer * * @param {Resource|String} urlOrResource The URL of the binary data. + * @param {Object} [headers] HTTP headers to send with the requests. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example @@ -33,27 +33,22 @@ define([ * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadArrayBuffer(urlOrResource, headers, request) { //>>includeStart('debug', pragmas.debug); Check.defined('urlOrResource', urlOrResource); //>>includeEnd('debug'); - if (defined(headers)) { - deprecationWarning('loadArrayBuffer.headers', 'The headers parameter has been deprecated. Set the headers property on the Resource parameter.'); - } - - if (defined(request)) { - deprecationWarning('loadArrayBuffer.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); - } + deprecationWarning('loadArrayBuffer', 'loadArrayBuffer is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchArrayBuffer instead.'); var resource = Resource.createIfNeeded(urlOrResource, { headers: headers, request: request }); - resource.responseType = 'arraybuffer'; - return loadWithXhr(resource); + return resource.fetchArrayBuffer(); } return loadArrayBuffer; diff --git a/Source/Core/loadBlob.js b/Source/Core/loadBlob.js index 553d617b279f..5523d30fa324 100644 --- a/Source/Core/loadBlob.js +++ b/Source/Core/loadBlob.js @@ -2,13 +2,11 @@ define([ './Check', './defined', './deprecationWarning', - './loadWithXhr', './Resource' ], function( Check, defined, deprecationWarning, - loadWithXhr, Resource) { 'use strict'; @@ -21,6 +19,8 @@ define([ * @exports loadBlob * * @param {Resource|String} urlOrResource The URL of the data. + * @param {Object} [headers] HTTP headers to send with the requests. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example @@ -33,27 +33,22 @@ define([ * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadBlob(urlOrResource, headers, request) { //>>includeStart('debug', pragmas.debug); Check.defined('urlOrResource', urlOrResource); //>>includeEnd('debug'); - if (defined(headers)) { - deprecationWarning('loadBlob.headers', 'The headers parameter has been deprecated. Set the headers property on the Resource parameter.'); - } - - if (defined(request)) { - deprecationWarning('loadBlob.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); - } + deprecationWarning('loadBlob', 'loadBlob is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchBlob instead.'); var resource = Resource.createIfNeeded(urlOrResource, { headers: headers, request: request }); - resource.responseType = 'blob'; - return loadWithXhr(resource); + return resource.fetchBlob(); } return loadBlob; diff --git a/Source/Core/loadCRN.js b/Source/Core/loadCRN.js index 4e878f712da8..74b4ef5180fe 100644 --- a/Source/Core/loadCRN.js +++ b/Source/Core/loadCRN.js @@ -4,7 +4,6 @@ define([ './defined', './deprecationWarning', './DeveloperError', - './loadArrayBuffer', './Resource', './TaskProcessor' ], function( @@ -13,7 +12,6 @@ define([ defined, deprecationWarning, DeveloperError, - loadArrayBuffer, Resource, TaskProcessor) { 'use strict'; @@ -69,12 +67,12 @@ define([ if (resourceOrUrlOrBuffer instanceof ArrayBuffer || ArrayBuffer.isView(resourceOrUrlOrBuffer)) { loadPromise = when.resolve(resourceOrUrlOrBuffer); } else { - resourceOrUrlOrBuffer = Resource.createIfNeeded(resourceOrUrlOrBuffer, { + var resource = Resource.createIfNeeded(resourceOrUrlOrBuffer, { headers: headers, request: request }); - loadPromise = loadArrayBuffer(resourceOrUrlOrBuffer); + loadPromise = resource.fetchArrayBuffer(); } if (!defined(loadPromise)) { diff --git a/Source/Core/loadImage.js b/Source/Core/loadImage.js index 1427f6bc8ed8..b347f5cc35ec 100644 --- a/Source/Core/loadImage.js +++ b/Source/Core/loadImage.js @@ -1,31 +1,15 @@ define([ - '../ThirdParty/when', './Check', - './defaultValue', './defined', + './defineProperties', './deprecationWarning', - './isBlobUri', - './isCrossOriginUrl', - './isDataUri', - './Request', - './RequestScheduler', - './RequestState', - './Resource', - './TrustedServers' + './Resource' ], function( - when, Check, - defaultValue, defined, + defineProperties, deprecationWarning, - isBlobUri, - isCrossOriginUrl, - isDataUri, - Request, - RequestScheduler, - RequestState, - Resource, - TrustedServers) { + Resource) { 'use strict'; /** @@ -35,6 +19,10 @@ define([ * @exports loadImage * * @param {Resource|String} urlOrResource The source URL of the image. + * @param {Boolean} [allowCrossOrigin=true] Whether to request the image using Cross-Origin + * Resource Sharing (CORS). CORS is only actually used if the image URL is actually cross-origin. + * Data URIs are never requested using CORS. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @@ -53,102 +41,39 @@ define([ * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadImage(urlOrResource, allowCrossOrigin, request) { //>>includeStart('debug', pragmas.debug); Check.defined('urlOrResource', urlOrResource); //>>includeEnd('debug'); - if (defined(allowCrossOrigin)) { - deprecationWarning('loadImage.allowCrossOrigin', 'The allowCrossOrigin parameter has been deprecated. It no longer needs to be specified.'); - } - - if (defined(request)) { - deprecationWarning('loadImage.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); - } - - // If the user specifies the request we should use it, not a cloned version, (createIfNeeded will clone the Resource). - request = defaultValue(urlOrResource.request, request); + deprecationWarning('loadImage', 'loadImage is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchImage instead.'); var resource = Resource.createIfNeeded(urlOrResource, { request: request }); - resource.request = defaultValue(resource.request, new Request()); - return makeRequest(resource, defaultValue(allowCrossOrigin, true)); + return resource.fetchImage(false, allowCrossOrigin); } - function makeRequest(resource, allowCrossOrigin) { - var request = resource.request; - request.url = resource.url; - request.requestFunction = function() { - var crossOrigin; - var url = resource.url; - - // data URIs can't have allowCrossOrigin set. - if (isDataUri(url) || isBlobUri(url)) { - crossOrigin = false; - } else { - crossOrigin = isCrossOriginUrl(url); + defineProperties(loadImage, { + createImage : { + get : function() { + return Resource._Implementations.createImage; + }, + set : function(value) { + Resource._Implementations.createImage = value; } + }, - var deferred = when.defer(); - - loadImage.createImage(url, crossOrigin && allowCrossOrigin, deferred); - - return deferred.promise; - }; - - var promise = RequestScheduler.request(request); - if (!defined(promise)) { - return; - } - - return promise - .otherwise(function(e) { - //Don't retry cancelled or otherwise aborted requests - if (request.state !== RequestState.FAILED) { - return when.reject(e); - } - - return resource.retryOnError(e) - .then(function(retry) { - if (retry) { - // Reset request so it can try again - request.state = RequestState.UNISSUED; - request.deferred = undefined; - - return makeRequest(resource, allowCrossOrigin); - } - return when.reject(e); - }); - }); - } - - // This is broken out into a separate function so that it can be mocked for testing purposes. - loadImage.createImage = function(url, crossOrigin, deferred) { - var image = new Image(); - - image.onload = function() { - deferred.resolve(image); - }; - - image.onerror = function(e) { - deferred.reject(e); - }; - - if (crossOrigin) { - if (TrustedServers.contains(url)) { - image.crossOrigin = 'use-credentials'; - } else { - image.crossOrigin = ''; + defaultCreateImage : { + get : function() { + return Resource._DefaultImplementations.createImage; } } - - image.src = url; - }; - - loadImage.defaultCreateImage = loadImage.createImage; + }); return loadImage; }); diff --git a/Source/Core/loadImageFromTypedArray.js b/Source/Core/loadImageFromTypedArray.js index 48b67a93165e..fa2df18dc4d7 100644 --- a/Source/Core/loadImageFromTypedArray.js +++ b/Source/Core/loadImageFromTypedArray.js @@ -1,12 +1,10 @@ define([ '../ThirdParty/when', './Check', - './loadImage', './Resource' ], function( when, Check, - loadImage, Resource) { 'use strict'; @@ -24,16 +22,18 @@ define([ }); var blobUrl = window.URL.createObjectURL(blob); - return loadImage(new Resource({ + var resource = new Resource({ url: blobUrl, request: request - })).then(function(image) { - window.URL.revokeObjectURL(blobUrl); - return image; - }, function(error) { - window.URL.revokeObjectURL(blobUrl); - return when.reject(error); }); + return resource.fetchImage() + .then(function(image) { + window.URL.revokeObjectURL(blobUrl); + return image; + }, function(error) { + window.URL.revokeObjectURL(blobUrl); + return when.reject(error); + }); } return loadImageFromTypedArray; diff --git a/Source/Core/loadImageViaBlob.js b/Source/Core/loadImageViaBlob.js index 9d0380e51c70..7b2003690d23 100644 --- a/Source/Core/loadImageViaBlob.js +++ b/Source/Core/loadImageViaBlob.js @@ -1,34 +1,15 @@ define([ - '../ThirdParty/when', './Check', './defined', './deprecationWarning', - './isDataUri', - './loadBlob', - './loadImage', './Resource' ], function( - when, Check, defined, deprecationWarning, - isDataUri, - loadBlob, - loadImage, Resource) { 'use strict'; - var xhrBlobSupported = (function() { - try { - var xhr = new XMLHttpRequest(); - xhr.open('GET', '#', true); - xhr.responseType = 'blob'; - return xhr.responseType === 'blob'; - } catch (e) { - return false; - } - })(); - /** * Asynchronously loads the given image URL by first downloading it as a blob using * XMLHttpRequest and then loading the image from the buffer via a blob URL. @@ -44,6 +25,7 @@ define([ * @exports loadImageViaBlob * * @param {Resource|String} urlOrResource The source URL of the image. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @@ -63,47 +45,21 @@ define([ * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadImageViaBlob(urlOrResource, request) { //>>includeStart('debug', pragmas.debug); Check.defined('urlOrResource', urlOrResource); //>>includeEnd('debug'); - if (defined(request)) { - deprecationWarning('loadCRN.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); - } + deprecationWarning('loadImageViaBlob', 'loadImageViaBlob is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchImage instead.'); var resource = Resource.createIfNeeded(urlOrResource, { request: request }); - var url = resource.url; - if (!xhrBlobSupported || isDataUri(url)) { - return loadImage(resource); - } - - var blobPromise = loadBlob(resource); - if (!defined(blobPromise)) { - return undefined; - } - - return blobPromise.then(function(blob) { - if (!defined(blob)) { - return; - } - var blobUrl = window.URL.createObjectURL(blob); - var blobResource = new Resource({ - url: blobUrl - }); - return loadImage(blobResource).then(function(image) { - image.blob = blob; - window.URL.revokeObjectURL(blobUrl); - return image; - }, function(error) { - window.URL.revokeObjectURL(blobUrl); - return when.reject(error); - }); - }); + return resource.fetchImage(true); } return loadImageViaBlob; diff --git a/Source/Core/loadJson.js b/Source/Core/loadJson.js index 7a849f74db88..6fabe58f5612 100644 --- a/Source/Core/loadJson.js +++ b/Source/Core/loadJson.js @@ -3,21 +3,15 @@ define([ './defined', './deprecationWarning', './DeveloperError', - './loadText', './Resource' ], function( clone, defined, deprecationWarning, DeveloperError, - loadText, Resource) { 'use strict'; - var defaultHeaders = { - Accept : 'application/json,*/*;q=0.01' - }; - // note: */* below is */* but that ends the comment block early /** * Asynchronously loads the given URL as JSON. Returns a promise that will resolve to @@ -30,6 +24,10 @@ define([ * @exports loadJson * * @param {Resource|String} urlOrResource The URL to request. + * @param {Object} [headers] HTTP headers to send with the request. + * 'Accept: application/json,*/*;q=0.01' is added to the request headers automatically + * if not specified. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @@ -43,6 +41,8 @@ define([ * @see loadText * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadJson(urlOrResource, headers, request) { //>>includeStart('debug', pragmas.debug); @@ -51,32 +51,14 @@ define([ } //>>includeEnd('debug'); - if (defined(headers)) { - deprecationWarning('loadJson.headers', 'The headers parameter has been deprecated. Set the headers property on the Resource parameter.'); - } - - if (defined(request)) { - deprecationWarning('loadJson.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); - } + deprecationWarning('loadJson', 'loadJson is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchJson instead.'); var resource = Resource.createIfNeeded(urlOrResource, { headers: headers, request: request }); - resource.headers.Accept = defaultHeaders.Accept; - - var textPromise = loadText(resource); - if (!defined(textPromise)) { - return undefined; - } - - return textPromise.then(function(value) { - if (!defined(value)) { - return; - } - return JSON.parse(value); - }); + return resource.fetchJson(); } return loadJson; diff --git a/Source/Core/loadJsonp.js b/Source/Core/loadJsonp.js index 65e3e4bc1d7a..a011ca37d9bd 100644 --- a/Source/Core/loadJsonp.js +++ b/Source/Core/loadJsonp.js @@ -1,32 +1,16 @@ define([ - '../ThirdParty/Uri', - '../ThirdParty/when', './clone', - './combine', - './defaultValue', './defined', + './defineProperties', './deprecationWarning', './DeveloperError', - './objectToQuery', - './queryToObject', - './Request', - './RequestScheduler', - './RequestState', './Resource' ], function( - Uri, - when, clone, - combine, - defaultValue, defined, + defineProperties, deprecationWarning, DeveloperError, - objectToQuery, - queryToObject, - Request, - RequestScheduler, - RequestState, Resource) { 'use strict'; @@ -37,18 +21,21 @@ define([ * * @param {Resource|String} urlOrResource The URL to request. * @param {String} [callbackParameterName='callback'] The callback parameter name that the server expects. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * // load a data asynchronously - * Cesium.loadJsonp('some/webservice').then(function(data) { + * resource.loadJsonp().then(function(data) { * // use the loaded data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadJsonp(urlOrResource, callbackParameterName, request) { //>>includeStart('debug', pragmas.debug); @@ -56,14 +43,12 @@ define([ throw new DeveloperError('urlOrResource is required.'); } //>>includeEnd('debug'); - if (defined(request)) { - deprecationWarning('loadJsonp.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); - } + + deprecationWarning('loadJsonp', 'loadJsonp is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchJsonp instead.'); var proxy; var queryParameters; if (typeof callbackParameterName === 'object') { - deprecationWarning('loadJsonp.callbackParameterName', 'Passing an Object as the second parameter is deprecated. The proxy and parameters options should now be set on the Resource instance.'); var options = callbackParameterName; if (defined(options.parameters)) { queryParameters = clone(options.parameters); @@ -73,93 +58,32 @@ define([ } callbackParameterName = options.callbackParameterName; } - callbackParameterName = defaultValue(callbackParameterName, 'callback'); - - //generate a unique function name - var functionName; - do { - functionName = 'loadJsonp' + Math.random().toString().substring(2, 8); - } while (defined(window[functionName])); var resource = Resource.createIfNeeded(urlOrResource, { proxy : proxy, queryParameters : queryParameters, request: request }); - resource.request = defaultValue(resource.request, new Request()); - return makeRequest(resource, callbackParameterName, functionName); + return resource.fetchJsonp(callbackParameterName); } - function makeRequest(resource, callbackParameterName, functionName) { - var callbackQuery = {}; - callbackQuery[callbackParameterName] = functionName; - resource.addQueryParameters(callbackQuery); - - var request = resource.request; - request.url = resource.url; - request.requestFunction = function() { - var deferred = when.defer(); - - //assign a function with that name in the global scope - window[functionName] = function(data) { - deferred.resolve(data); - - try { - delete window[functionName]; - } catch (e) { - window[functionName] = undefined; - } - }; - - loadJsonp.loadAndExecuteScript(resource.url, functionName, deferred); - return deferred.promise; - }; + defineProperties(loadJsonp, { + loadAndExecuteScript : { + get : function() { + return Resource._Implementations.loadAndExecuteScript; + }, + set : function(value) { + Resource._Implementations.loadAndExecuteScript = value; + } + }, - var promise = RequestScheduler.request(request); - if (!defined(promise)) { - return; + defaultLoadAndExecuteScript : { + get : function() { + return Resource._DefaultImplementations.loadAndExecuteScript; + } } - - return promise - .otherwise(function(e) { - if (request.state !== RequestState.FAILED) { - return when.reject(e); - } - return resource.retryOnError(e) - .then(function(retry) { - if (retry) { - // Reset request so it can try again - request.state = RequestState.UNISSUED; - request.deferred = undefined; - - return makeRequest(resource, callbackParameterName, functionName); - } - - return when.reject(e); - }); - }); - } - - // This is broken out into a separate function so that it can be mocked for testing purposes. - loadJsonp.loadAndExecuteScript = function(url, functionName, deferred) { - var script = document.createElement('script'); - script.async = true; - script.src = url; - - var head = document.getElementsByTagName('head')[0]; - script.onload = function() { - script.onload = undefined; - head.removeChild(script); - }; - script.onerror = function(e) { - deferred.reject(e); - }; - - head.appendChild(script); - }; - - loadJsonp.defaultLoadAndExecuteScript = loadJsonp.loadAndExecuteScript; + }); return loadJsonp; }); diff --git a/Source/Core/loadKTX.js b/Source/Core/loadKTX.js index 0348bc959c04..bd3212922994 100644 --- a/Source/Core/loadKTX.js +++ b/Source/Core/loadKTX.js @@ -3,7 +3,7 @@ define([ './Check', './CompressedTextureBuffer', './defined', - './loadArrayBuffer', + './deprecationWarning', './PixelFormat', './Resource', './RuntimeError' @@ -12,7 +12,7 @@ define([ Check, CompressedTextureBuffer, defined, - loadArrayBuffer, + deprecationWarning, PixelFormat, Resource, RuntimeError) { @@ -39,8 +39,6 @@ define([ * @exports loadKTX * * @param {Resource|String|ArrayBuffer} resourceOrUrlOrBuffer The URL of the binary data or an ArrayBuffer. - * @param {Object} [headers] HTTP headers to send with the requests. - * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @exception {RuntimeError} Invalid KTX file. @@ -76,18 +74,23 @@ define([ Check.defined('resourceOrUrlOrBuffer', resourceOrUrlOrBuffer); //>>includeEnd('debug'); + if (defined(headers)) { + deprecationWarning('loadCRN.headers', 'The headers parameter has been deprecated. Set the headers property on the Resource parameter.'); + } + + if (defined(request)) { + deprecationWarning('loadCRN.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); + } + var loadPromise; if (resourceOrUrlOrBuffer instanceof ArrayBuffer || ArrayBuffer.isView(resourceOrUrlOrBuffer)) { loadPromise = when.resolve(resourceOrUrlOrBuffer); } else { - if (typeof resourceOrUrlOrBuffer === 'string') { - resourceOrUrlOrBuffer = new Resource({ - url: resourceOrUrlOrBuffer, - headers: headers, - request: request - }); - } - loadPromise = loadArrayBuffer(resourceOrUrlOrBuffer); + var resource = Resource.createIfNeeded(resourceOrUrlOrBuffer, { + headers: headers, + request: request + }); + loadPromise = resource.fetchArrayBuffer(); } if (!defined(loadPromise)) { diff --git a/Source/Core/loadText.js b/Source/Core/loadText.js index d4b2dd6cc704..1aab479c89de 100644 --- a/Source/Core/loadText.js +++ b/Source/Core/loadText.js @@ -2,13 +2,11 @@ define([ './Check', './defined', './deprecationWarning', - './loadWithXhr', './Resource' ], function( Check, defined, deprecationWarning, - loadWithXhr, Resource) { 'use strict'; @@ -21,6 +19,8 @@ define([ * @exports loadText * * @param {Resource|String} urlOrResource The URL to request. + * @param {Object} [headers] HTTP headers to send with the requests. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @@ -41,26 +41,22 @@ define([ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadText(urlOrResource, headers, request) { //>>includeStart('debug', pragmas.debug); Check.defined('urlOrResource', urlOrResource); //>>includeEnd('debug'); - if (defined(headers)) { - deprecationWarning('loadText.headers', 'The headers parameter has been deprecated. Set the headers property on the Resource parameter.'); - } - - if (defined(request)) { - deprecationWarning('loadText.request', 'The request parameter has been deprecated. Set the request property on the Resource parameter.'); - } + deprecationWarning('loadText', 'loadText is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchText instead.'); var resource = Resource.createIfNeeded(urlOrResource, { headers: headers, request: request }); - return loadWithXhr(resource); + return resource.fetchText(); } return loadText; diff --git a/Source/Core/loadWithXhr.js b/Source/Core/loadWithXhr.js index 6e51a6928412..c7cd6f2491fe 100644 --- a/Source/Core/loadWithXhr.js +++ b/Source/Core/loadWithXhr.js @@ -1,31 +1,15 @@ define([ '../ThirdParty/when', './Check', - './clone', - './defaultValue', - './defined', - './DeveloperError', - './Request', - './RequestErrorEvent', - './RequestScheduler', - './RequestState', - './Resource', - './RuntimeError', - './TrustedServers' + './defineProperties', + './deprecationWarning', + './Resource' ], function( when, Check, - clone, - defaultValue, - defined, - DeveloperError, - Request, - RequestErrorEvent, - RequestScheduler, - RequestState, - Resource, - RuntimeError, - TrustedServers) { + defineProperties, + deprecationWarning, + Resource) { 'use strict'; /** @@ -36,14 +20,14 @@ define([ * * @exports loadWithXhr * - * @param {Resource|Object} optionsOrResource Object with the following properties: - * @param {String} optionsOrResource.url The URL of the data. - * @param {String} [optionsOrResource.responseType] The type of response. This controls the type of item returned. - * @param {String} [optionsOrResource.method='GET'] The HTTP method to use. - * @param {String} [optionsOrResource.data] The data to send with the request, if any. - * @param {Object} [optionsOrResource.headers] HTTP headers to send with the request, if any. - * @param {String} [optionsOrResource.overrideMimeType] Overrides the MIME type returned by the server. - * @param {Request} [optionsOrResource.request] The request object. + * @param {Object} options Object with the following properties: + * @param {Resource|String} options.url The URL of the data. + * @param {String} [options.responseType] The type of response. This controls the type of item returned. + * @param {String} [options.method='GET'] The HTTP method to use. + * @param {String} [options.data] The data to send with the request, if any. + * @param {Object} [options.headers] HTTP headers to send with the request, if any. + * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. + * @param {Request} [options.request] The request object. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @@ -64,206 +48,41 @@ define([ * @see loadText * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ - - function loadWithXhr(optionsOrResource) { + function loadWithXhr(options) { //>>includeStart('debug', pragmas.debug); - Check.defined('optionsOrResource', optionsOrResource); + Check.defined('options', options); //>>includeEnd('debug'); - var resource; - if (optionsOrResource instanceof Resource) { - resource = optionsOrResource.clone(); - } else { - // Take advantage that the options are the same - resource = new Resource(optionsOrResource); - } + deprecationWarning('loadWithXhr', 'loadWithXhr is deprecated and will be removed in Cesium 1.44. Please use Resource.fetch instead.'); - if (!defined(resource.request)) { - resource.request = new Request(); - } + // Take advantage that most parameters are the same + var resource = new Resource(options); - return makeRequest(resource); + return resource.fetch({ + responseType: options.responseType, + overrideMimeType: options.overrideMimeType + }); } - function makeRequest(optionsOrResource) { - var request = optionsOrResource.request; - request.url = optionsOrResource.url; - - request.requestFunction = function() { - var responseType = optionsOrResource.responseType; - var method = optionsOrResource.method; - var data = optionsOrResource.data; - var headers = optionsOrResource.headers; - var overrideMimeType = optionsOrResource.overrideMimeType; - var deferred = when.defer(); - var xhr = loadWithXhr.load(optionsOrResource.url, responseType, method, data, headers, deferred, overrideMimeType); - if (defined(xhr) && defined(xhr.abort)) { - request.cancelFunction = function() { - xhr.abort(); - }; + defineProperties(loadWithXhr, { + load : { + get : function() { + return Resource._Implementations.loadWithXhr; + }, + set : function(value) { + Resource._Implementations.loadWithXhr = value; } - return deferred.promise; - }; - - var promise = RequestScheduler.request(request); - if (!defined(promise)) { - return; - } - - return promise - .then(function(data) { - return data; - }) - .otherwise(function(e) { - if ((request.state !== RequestState.FAILED) || !defined(optionsOrResource.retryOnError)) { - return when.reject(e); - } - - return optionsOrResource.retryOnError(e) - .then(function(retry) { - if (retry) { - // Reset request so it can try again - request.state = RequestState.UNISSUED; - request.deferred = undefined; - - return makeRequest(optionsOrResource); - } - - return when.reject(e); - }); - }); - } - - var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - - function decodeDataUriText(isBase64, data) { - var result = decodeURIComponent(data); - if (isBase64) { - return atob(result); - } - return result; - } - - function decodeDataUriArrayBuffer(isBase64, data) { - var byteString = decodeDataUriText(isBase64, data); - var buffer = new ArrayBuffer(byteString.length); - var view = new Uint8Array(buffer); - for (var i = 0; i < byteString.length; i++) { - view[i] = byteString.charCodeAt(i); - } - return buffer; - } - - function decodeDataUri(dataUriRegexResult, responseType) { - responseType = defaultValue(responseType, ''); - var mimeType = dataUriRegexResult[1]; - var isBase64 = !!dataUriRegexResult[2]; - var data = dataUriRegexResult[3]; - - switch (responseType) { - case '': - case 'text': - return decodeDataUriText(isBase64, data); - case 'arraybuffer': - return decodeDataUriArrayBuffer(isBase64, data); - case 'blob': - var buffer = decodeDataUriArrayBuffer(isBase64, data); - return new Blob([buffer], { - type : mimeType - }); - case 'document': - var parser = new DOMParser(); - return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType); - case 'json': - return JSON.parse(decodeDataUriText(isBase64, data)); - default: - //>>includeStart('debug', pragmas.debug); - throw new DeveloperError('Unhandled responseType: ' + responseType); - //>>includeEnd('debug'); - } - } - - // This is broken out into a separate function so that it can be mocked for testing purposes. - loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var dataUriRegexResult = dataUriRegex.exec(url); - if (dataUriRegexResult !== null) { - deferred.resolve(decodeDataUri(dataUriRegexResult, responseType)); - return; - } - - var xhr = new XMLHttpRequest(); - - if (TrustedServers.contains(url)) { - xhr.withCredentials = true; - } + }, - if (defined(overrideMimeType) && defined(xhr.overrideMimeType)) { - xhr.overrideMimeType(overrideMimeType); - } - - xhr.open(method, url, true); - - if (defined(headers)) { - for (var key in headers) { - if (headers.hasOwnProperty(key)) { - xhr.setRequestHeader(key, headers[key]); - } + defaultLoad : { + get : function() { + return Resource._DefaultImplementations.loadWithXhr; } } - - if (defined(responseType)) { - xhr.responseType = responseType; - } - - // While non-standard, file protocol always returns a status of 0 on success - var localFile = false; - if (typeof url === 'string') { - localFile = url.indexOf('file://') === 0; - } - - xhr.onload = function() { - if ((xhr.status < 200 || xhr.status >= 300) && !(localFile && xhr.status === 0)) { - deferred.reject(new RequestErrorEvent(xhr.status, xhr.response, xhr.getAllResponseHeaders())); - return; - } - - var response = xhr.response; - var browserResponseType = xhr.responseType; - - //All modern browsers will go into either the first or second if block or last else block. - //Other code paths support older browsers that either do not support the supplied responseType - //or do not support the xhr.response property. - if (xhr.status === 204) { - // accept no content - deferred.resolve(); - } else if (defined(response) && (!defined(responseType) || (browserResponseType === responseType))) { - deferred.resolve(response); - } else if ((responseType === 'json') && typeof response === 'string') { - try { - deferred.resolve(JSON.parse(response)); - } catch (e) { - deferred.reject(e); - } - } else if ((browserResponseType === '' || browserResponseType === 'document') && defined(xhr.responseXML) && xhr.responseXML.hasChildNodes()) { - deferred.resolve(xhr.responseXML); - } else if ((browserResponseType === '' || browserResponseType === 'text') && defined(xhr.responseText)) { - deferred.resolve(xhr.responseText); - } else { - deferred.reject(new RuntimeError('Invalid XMLHttpRequest response type.')); - } - }; - - xhr.onerror = function(e) { - deferred.reject(new RequestErrorEvent()); - }; - - xhr.send(data); - - return xhr; - }; - - loadWithXhr.defaultLoad = loadWithXhr.load; + }); return loadWithXhr; }); diff --git a/Source/Core/loadXML.js b/Source/Core/loadXML.js index ade11f2a852a..b3291f153f2e 100644 --- a/Source/Core/loadXML.js +++ b/Source/Core/loadXML.js @@ -1,10 +1,12 @@ define([ './Check', - './loadWithXhr', + './defined', + './deprecationWarning', './Resource' ], function( Check, - loadWithXhr, + defined, + deprecationWarning, Resource) { 'use strict'; @@ -17,8 +19,8 @@ define([ * @exports loadXML * * @param {Resource|String} urlOrResource The URL to request. - * @param {Object} [headers] HTTP headers to send with the request. // TODO: Do we want to deprecate? - * @param {Request} [request] The request object. Intended for internal use only. // TODO: Do we want to deprecate? + * @param {Object} [headers] HTTP headers to send with the requests. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @@ -35,20 +37,22 @@ define([ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} + * + * @deprecated */ function loadXML(urlOrResource, headers, request) { //>>includeStart('debug', pragmas.debug); Check.defined('urlOrResource', urlOrResource); //>>includeEnd('debug'); + deprecationWarning('loadXML', 'loadXML is deprecated and will be removed in Cesium 1.44. Please use Resource.fetchXML instead.'); + var resource = Resource.createIfNeeded(urlOrResource, { headers: headers, request: request }); - resource.responseType = 'document'; - resource.overrideMimeType = 'text/xml'; - return loadWithXhr(resource); + return resource.fetchXML(); } return loadXML; diff --git a/Source/DataSources/CzmlDataSource.js b/Source/DataSources/CzmlDataSource.js index 4ec61dbb0467..ea7e203c46fd 100644 --- a/Source/DataSources/CzmlDataSource.js +++ b/Source/DataSources/CzmlDataSource.js @@ -24,7 +24,6 @@ define([ '../Core/JulianDate', '../Core/LagrangePolynomialApproximation', '../Core/LinearApproximation', - '../Core/loadJson', '../Core/Math', '../Core/NearFarScalar', '../Core/Quaternion', @@ -113,7 +112,6 @@ define([ JulianDate, LagrangePolynomialApproximation, LinearApproximation, - loadJson, CesiumMath, NearFarScalar, Quaternion, @@ -1987,7 +1985,7 @@ define([ queryParameters: query }); - promise = loadJson(czml); + promise = czml.fetchJson(); sourceUri = defaultValue(sourceUri, czml.clone()); } diff --git a/Source/DataSources/GeoJsonDataSource.js b/Source/DataSources/GeoJsonDataSource.js index 8dd8b24fc271..8522c3fb45eb 100644 --- a/Source/DataSources/GeoJsonDataSource.js +++ b/Source/DataSources/GeoJsonDataSource.js @@ -8,7 +8,6 @@ define([ '../Core/DeveloperError', '../Core/Event', '../Core/getFilenameFromUri', - '../Core/loadJson', '../Core/PinBuilder', '../Core/PolygonHierarchy', '../Core/Resource', @@ -38,7 +37,6 @@ define([ DeveloperError, Event, getFilenameFromUri, - loadJson, PinBuilder, PolygonHierarchy, Resource, @@ -827,7 +825,7 @@ define([ if (typeof data === 'string' || (data instanceof Resource)) { data = Resource.createIfNeeded(data); - promise = loadJson(data); + promise = data.fetchJson(); sourceUri = defaultValue(sourceUri, data.getUrlComponent()); } diff --git a/Source/DataSources/KmlDataSource.js b/Source/DataSources/KmlDataSource.js index a349faf3dfba..203fb879a002 100644 --- a/Source/DataSources/KmlDataSource.js +++ b/Source/DataSources/KmlDataSource.js @@ -19,8 +19,6 @@ define([ '../Core/getFilenameFromUri', '../Core/Iso8601', '../Core/JulianDate', - '../Core/loadBlob', - '../Core/loadXML', '../Core/Math', '../Core/NearFarScalar', '../Core/objectToQuery', @@ -88,8 +86,6 @@ define([ getFilenameFromUri, Iso8601, JulianDate, - loadBlob, - loadXML, CesiumMath, NearFarScalar, objectToQuery, @@ -966,7 +962,7 @@ define([ //Asynchronously processes an external style file. function processExternalStyles(dataSource, resource, styleCollection) { - return loadXML(resource).then(function(styleKml) { + return resource.fetchXML().then(function(styleKml) { return processStyles(dataSource, styleKml, styleCollection, resource, true); }); } @@ -2379,7 +2375,7 @@ define([ queryParameters: query }); - promise = loadBlob(data); + promise = data.fetchBlob(); sourceUri = defaultValue(sourceUri, data.clone()); } else { diff --git a/Source/Renderer/loadCubeMap.js b/Source/Renderer/loadCubeMap.js index 6b1e6a8638ec..b0a682bd0caf 100644 --- a/Source/Renderer/loadCubeMap.js +++ b/Source/Renderer/loadCubeMap.js @@ -2,7 +2,6 @@ define([ '../Core/Check', '../Core/defined', '../Core/DeveloperError', - '../Core/loadImage', '../Core/Resource', '../ThirdParty/when', './CubeMap' @@ -10,7 +9,6 @@ define([ Check, defined, DeveloperError, - loadImage, Resource, when, CubeMap) { @@ -70,24 +68,12 @@ define([ // ideally, we would do it in the primitive's update function. var facePromises = [ - loadImage(new Resource({ - url: urls.positiveX - })), - loadImage(new Resource({ - url: urls.negativeX - })), - loadImage(new Resource({ - url: urls.positiveY - })), - loadImage(new Resource({ - url: urls.negativeY - })), - loadImage(new Resource({ - url: urls.positiveZ - })), - loadImage(new Resource({ - url: urls.negativeZ - })) + Resource.createIfNeeded(urls.positiveX).fetchImage(), + Resource.createIfNeeded(urls.negativeX).fetchImage(), + Resource.createIfNeeded(urls.positiveY).fetchImage(), + Resource.createIfNeeded(urls.negativeY).fetchImage(), + Resource.createIfNeeded(urls.positiveZ).fetchImage(), + Resource.createIfNeeded(urls.negativeZ).fetchImage() ]; return when.all(facePromises, function(images) { diff --git a/Source/Scene/ArcGisMapServerImageryProvider.js b/Source/Scene/ArcGisMapServerImageryProvider.js index 174e9f2ce956..c6a6cf8d06b5 100644 --- a/Source/Scene/ArcGisMapServerImageryProvider.js +++ b/Source/Scene/ArcGisMapServerImageryProvider.js @@ -10,8 +10,6 @@ define([ '../Core/DeveloperError', '../Core/Event', '../Core/GeographicTilingScheme', - '../Core/loadJson', - '../Core/loadJsonp', '../Core/Math', '../Core/Rectangle', '../Core/Resource', @@ -35,8 +33,6 @@ define([ DeveloperError, Event, GeographicTilingScheme, - loadJson, - loadJsonp, CesiumMath, Rectangle, Resource, @@ -240,7 +236,7 @@ define([ f: 'json' } }); - var metadata = loadJsonp(resource); + var metadata = resource.fetchJsonp(); when(metadata, metadataSuccess, metadataFailure); } @@ -667,7 +663,7 @@ define([ queryParameters: query }); - return loadJson(resource).then(function(json) { + return resource.fetchJson().then(function(json) { var result = []; var features = json.results; diff --git a/Source/Scene/BingMapsImageryProvider.js b/Source/Scene/BingMapsImageryProvider.js index 77a505912b56..14a284d1645e 100644 --- a/Source/Scene/BingMapsImageryProvider.js +++ b/Source/Scene/BingMapsImageryProvider.js @@ -8,7 +8,6 @@ define([ '../Core/deprecationWarning', '../Core/DeveloperError', '../Core/Event', - '../Core/loadJsonp', '../Core/Math', '../Core/Rectangle', '../Core/Resource', @@ -29,7 +28,6 @@ define([ deprecationWarning, DeveloperError, Event, - loadJsonp, CesiumMath, Rectangle, Resource, @@ -236,7 +234,7 @@ define([ } function requestMetadata() { - var metadata = loadJsonp(metadataResource, 'jsonp'); + var metadata = metadataResource.fetchJsonp('jsonp'); when(metadata, metadataSuccess, metadataFailure); } diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 10f613ed816e..be37ce87d547 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -11,7 +11,6 @@ define([ '../Core/getMagic', '../Core/Intersect', '../Core/JulianDate', - '../Core/loadArrayBuffer', '../Core/Matrix3', '../Core/Matrix4', '../Core/Plane', @@ -46,7 +45,6 @@ define([ getMagic, Intersect, JulianDate, - loadArrayBuffer, Matrix3, Matrix4, Plane, @@ -636,7 +634,7 @@ define([ resource.request = request; - var promise = loadArrayBuffer(resource); + var promise = resource.fetchArrayBuffer(); if (!defined(promise)) { return false; diff --git a/Source/Scene/Cesium3DTileStyle.js b/Source/Scene/Cesium3DTileStyle.js index 7e4841eca9a9..4f110da1435c 100644 --- a/Source/Scene/Cesium3DTileStyle.js +++ b/Source/Scene/Cesium3DTileStyle.js @@ -4,7 +4,6 @@ define([ '../Core/defined', '../Core/defineProperties', '../Core/DeveloperError', - '../Core/loadJson', '../Core/Resource', '../ThirdParty/when', './ConditionsExpression', @@ -15,7 +14,6 @@ define([ defined, defineProperties, DeveloperError, - loadJson, Resource, when, ConditionsExpression, @@ -100,7 +98,8 @@ define([ var promise; if (typeof style === 'string' || style instanceof Resource) { - promise = loadJson(style); + var resource = Resource.createIfNeeded(style); + promise = resource.fetchJson(style); } else { promise = when.resolve(style); } diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index b67e5ad02032..81ee88ae968b 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -17,8 +17,6 @@ define([ '../Core/getMagic', '../Core/isDataUri', '../Core/JulianDate', - '../Core/loadArrayBuffer', - '../Core/loadJson', '../Core/ManagedArray', '../Core/Math', '../Core/Matrix4', @@ -62,8 +60,6 @@ define([ getMagic, isDataUri, JulianDate, - loadArrayBuffer, - loadJson, ManagedArray, CesiumMath, Matrix4, @@ -738,7 +734,7 @@ define([ url : testUrl }); - return loadArrayBuffer(testResource) + return testResource.fetchArrayBuffer() .then(function(buffer) { var uint8Array = new Uint8Array(buffer); var magic = getMagic(uint8Array); @@ -1224,7 +1220,8 @@ define([ * @returns {Promise.} A promise that resolves with the fetched json data */ Cesium3DTileset.loadJson = function(tilesetUrl) { - return loadJson(tilesetUrl); + var resource = Resource.createIfNeeded(tilesetUrl); + return resource.fetchJson(); }; /** diff --git a/Source/Scene/CesiumIon.js b/Source/Scene/CesiumIon.js index bd7771d6a501..28540e4f41c8 100644 --- a/Source/Scene/CesiumIon.js +++ b/Source/Scene/CesiumIon.js @@ -272,7 +272,9 @@ define([ }; //Exposed for testing - CesiumIon._loadJson = loadJson; + CesiumIon._loadJson = function(resource) { + return resource.fetchJson(); + }; return CesiumIon; }); diff --git a/Source/Scene/ClassificationModel.js b/Source/Scene/ClassificationModel.js index 61a3709e5d81..f5955f6bb818 100644 --- a/Source/Scene/ClassificationModel.js +++ b/Source/Scene/ClassificationModel.js @@ -14,7 +14,6 @@ define([ '../Core/DeveloperError', '../Core/FeatureDetection', '../Core/IndexDatatype', - '../Core/loadArrayBuffer', '../Core/Matrix4', '../Core/PrimitiveType', '../Core/Quaternion', @@ -50,7 +49,6 @@ define([ DeveloperError, FeatureDetection, IndexDatatype, - loadArrayBuffer, Matrix4, PrimitiveType, Quaternion, @@ -572,7 +570,7 @@ define([ url : buffer.uri }); ++loadResources.pendingBufferLoads; - loadArrayBuffer(bufferResource).then(bufferLoad(model, i)).otherwise(getFailedLoadFunction(model, 'buffer', bufferResource.uri)); + bufferResource.fetchArrayBuffer().then(bufferLoad(model, i)).otherwise(getFailedLoadFunction(model, 'buffer', bufferResource.uri)); } } } diff --git a/Source/Scene/DiscardMissingTileImagePolicy.js b/Source/Scene/DiscardMissingTileImagePolicy.js index 3cfab527b238..ef4c66a18dfb 100644 --- a/Source/Scene/DiscardMissingTileImagePolicy.js +++ b/Source/Scene/DiscardMissingTileImagePolicy.js @@ -3,7 +3,6 @@ define([ '../Core/defined', '../Core/DeveloperError', '../Core/getImagePixels', - '../Core/loadImageViaBlob', '../Core/Resource', '../ThirdParty/when' ], function( @@ -11,7 +10,6 @@ define([ defined, DeveloperError, getImagePixels, - loadImageViaBlob, Resource, when) { 'use strict'; @@ -49,6 +47,8 @@ define([ this._missingImageByteLength = undefined; this._isReady = false; + var resource = Resource.createIfNeeded(options.missingImageUrl); + var that = this; function success(image) { @@ -89,9 +89,7 @@ define([ that._isReady = true; } - var resource = Resource.createIfNeeded(options.missingImageUrl); - - when(loadImageViaBlob(resource), success, failure); + when(resource.fetchImage(true), success, failure); } /** diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 8e0996b45059..2b98a8dd0fa0 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -12,7 +12,6 @@ define([ '../Core/EllipsoidTerrainProvider', '../Core/Event', '../Core/IntersectionTests', - '../Core/loadImage', '../Core/Ray', '../Core/Rectangle', '../Core/Resource', @@ -43,7 +42,6 @@ define([ EllipsoidTerrainProvider, Event, IntersectionTests, - loadImage, Ray, Rectangle, Resource, @@ -578,7 +576,7 @@ define([ var oceanNormalMapUrl = oceanNormalMapResource.url; if (defined(oceanNormalMapUrl)) { var that = this; - when(loadImage(oceanNormalMapResource), function(image) { + when(oceanNormalMapResource.fetchImage(), function(image) { if (oceanNormalMapUrl !== that._oceanNormalMapResource.url) { // url changed while we were loading return; diff --git a/Source/Scene/GoogleEarthEnterpriseImageryProvider.js b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js index 4046c98a706d..e3602588038d 100644 --- a/Source/Scene/GoogleEarthEnterpriseImageryProvider.js +++ b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js @@ -9,7 +9,6 @@ define([ '../Core/Event', '../Core/GeographicTilingScheme', '../Core/GoogleEarthEnterpriseMetadata', - '../Core/loadArrayBuffer', '../Core/loadImageFromTypedArray', '../Core/Math', '../Core/Rectangle', @@ -30,7 +29,6 @@ define([ Event, GeographicTilingScheme, GoogleEarthEnterpriseMetadata, - loadArrayBuffer, loadImageFromTypedArray, CesiumMath, Rectangle, @@ -472,7 +470,7 @@ define([ // Already have info and there isn't any imagery here return invalidImage; } - var promise = loadArrayBuffer(buildImageResource(this, info, x, y, level, request)); + var promise = buildImageResource(this, info, x, y, level, request).fetchArrayBuffer(); if (!defined(promise)) { return undefined; // Throttled } diff --git a/Source/Scene/GoogleEarthEnterpriseMapsProvider.js b/Source/Scene/GoogleEarthEnterpriseMapsProvider.js index d2fa6fe389e3..a1dbe9a1bd35 100644 --- a/Source/Scene/GoogleEarthEnterpriseMapsProvider.js +++ b/Source/Scene/GoogleEarthEnterpriseMapsProvider.js @@ -7,7 +7,6 @@ define([ '../Core/DeveloperError', '../Core/Event', '../Core/GeographicTilingScheme', - '../Core/loadText', '../Core/Rectangle', '../Core/Resource', '../Core/RuntimeError', @@ -24,7 +23,6 @@ define([ DeveloperError, Event, GeographicTilingScheme, - loadText, Rectangle, Resource, RuntimeError, @@ -238,7 +236,7 @@ define([ } function requestMetadata() { - var metadata = loadText(metadataResource); + var metadata = metadataResource.fetchText(); when(metadata, metadataSuccess, metadataFailure); } diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js index 91ab50a5f457..1610b78e82e8 100644 --- a/Source/Scene/GroundPrimitive.js +++ b/Source/Scene/GroundPrimitive.js @@ -12,7 +12,6 @@ define([ '../Core/GeographicTilingScheme', '../Core/GeometryInstance', '../Core/isArray', - '../Core/loadJson', '../Core/Math', '../Core/OrientedBoundingBox', '../Core/Rectangle', @@ -36,7 +35,6 @@ define([ GeographicTilingScheme, GeometryInstance, isArray, - loadJson, CesiumMath, OrientedBoundingBox, Rectangle, @@ -669,7 +667,7 @@ define([ return initPromise; } - GroundPrimitive._initPromise = loadJson(terrainHeightsResource).then(function(json) { + GroundPrimitive._initPromise = terrainHeightsResource.fetchJson().then(function(json) { GroundPrimitive._initialized = true; GroundPrimitive._terrainHeights = json; }); diff --git a/Source/Scene/ImageryProvider.js b/Source/Scene/ImageryProvider.js index 00bae7063dde..ee8e77b37f30 100644 --- a/Source/Scene/ImageryProvider.js +++ b/Source/Scene/ImageryProvider.js @@ -5,8 +5,6 @@ define([ '../Core/deprecationWarning', '../Core/DeveloperError', '../Core/loadCRN', - '../Core/loadImage', - '../Core/loadImageViaBlob', '../Core/loadKTX', '../Core/Resource' ], function( @@ -16,8 +14,6 @@ define([ deprecationWarning, DeveloperError, loadCRN, - loadImage, - loadImageViaBlob, loadKTX, Resource) { 'use strict'; @@ -356,10 +352,10 @@ define([ } else if (crnRegex.test(resource)) { return loadCRN(resource); } else if (defined(imageryProvider.tileDiscardPolicy)) { - return loadImageViaBlob(resource); + return resource.fetchImage(true); } - return loadImage(resource); + return resource.fetchImage(); }; return ImageryProvider; diff --git a/Source/Scene/Material.js b/Source/Scene/Material.js index 34dd35b90a03..6d7dd1d1456b 100644 --- a/Source/Scene/Material.js +++ b/Source/Scene/Material.js @@ -11,7 +11,6 @@ define([ '../Core/DeveloperError', '../Core/isArray', '../Core/loadCRN', - '../Core/loadImage', '../Core/loadKTX', '../Core/Matrix2', '../Core/Matrix3', @@ -49,7 +48,6 @@ define([ DeveloperError, isArray, loadCRN, - loadImage, loadKTX, Matrix2, Matrix3, @@ -803,7 +801,7 @@ define([ } else if (crnRegex.test(uniformValue)) { promise = loadCRN(resource); } else { - promise = loadImage(resource); + promise = resource.fetchImage(); } when(promise, function(image) { material._loadedImages.push({ @@ -853,12 +851,12 @@ define([ if (path !== material._texturePaths[uniformId]) { var promises = [ - loadImage(new Resource({url: uniformValue.positiveX})), - loadImage(new Resource({url: uniformValue.negativeX})), - loadImage(new Resource({url: uniformValue.positiveY})), - loadImage(new Resource({url: uniformValue.negativeY})), - loadImage(new Resource({url: uniformValue.positiveZ})), - loadImage(new Resource({url: uniformValue.negativeZ})) + Resource.createIfNeeded(uniformValue.positiveX).fetchImage(), + Resource.createIfNeeded(uniformValue.negativeX).fetchImage(), + Resource.createIfNeeded(uniformValue.positiveY).fetchImage(), + Resource.createIfNeeded(uniformValue.negativeY).fetchImage(), + Resource.createIfNeeded(uniformValue.positiveZ).fetchImage(), + Resource.createIfNeeded(uniformValue.negativeZ).fetchImage() ]; when.all(promises).then(function(images) { diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 6457c7006e52..a895bb838a7f 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -20,12 +20,9 @@ define([ '../Core/getMagic', '../Core/getStringFromTypedArray', '../Core/IndexDatatype', - '../Core/loadArrayBuffer', '../Core/loadCRN', - '../Core/loadImage', '../Core/loadImageFromTypedArray', '../Core/loadKTX', - '../Core/loadText', '../Core/Math', '../Core/Matrix2', '../Core/Matrix3', @@ -99,12 +96,9 @@ define([ getMagic, getStringFromTypedArray, IndexDatatype, - loadArrayBuffer, loadCRN, - loadImage, loadImageFromTypedArray, loadKTX, - loadText, CesiumMath, Matrix2, Matrix3, @@ -1152,7 +1146,7 @@ define([ modelResource.headers.Accept = defaultModelAccept; } - loadArrayBuffer(modelResource).then(function(arrayBuffer) { + modelResource.fetchArrayBuffer().then(function(arrayBuffer) { var array = new Uint8Array(arrayBuffer); if (containsGltfMagic(array)) { // Load binary glTF @@ -1166,7 +1160,7 @@ define([ } }).otherwise(getFailedLoadFunction(model, 'model', url)); } else if (!cachedGltf.ready) { - // Cache hit but the loadArrayBuffer() or loadText() request is still pending + // Cache hit but the fetchArrayBuffer() or fetchText() request is still pending ++cachedGltf.count; cachedGltf.modelsToLoad.push(model); } @@ -1349,7 +1343,7 @@ define([ url : buffer.uri }); ++loadResources.pendingBufferLoads; - loadArrayBuffer(bufferResource).then(bufferLoad(model, id)).otherwise(getFailedLoadFunction(model, 'buffer', bufferResource.url)); + bufferResource.fetchArrayBuffer().then(bufferLoad(model, id)).otherwise(getFailedLoadFunction(model, 'buffer', bufferResource.url)); } } } @@ -1429,7 +1423,7 @@ define([ url : shader.uri }); - loadText(shaderResource).then(shaderLoad(model, shader.type, id)).otherwise(getFailedLoadFunction(model, 'shader', shaderResource.url)); + shaderResource.fetchText().then(shaderLoad(model, shader.type, id)).otherwise(getFailedLoadFunction(model, 'shader', shaderResource.url)); } }); } @@ -1532,7 +1526,7 @@ define([ } else if (crnRegex.test(uri)) { promise = loadCRN(imageResource); } else { - promise = loadImage(imageResource); + promise = imageResource.fetchImage(); } promise.then(imageLoad(model, id, imageId)).otherwise(getFailedLoadFunction(model, 'image', imageResource.url)); } diff --git a/Source/Scene/SingleTileImageryProvider.js b/Source/Scene/SingleTileImageryProvider.js index 6520ec14905e..2a2e676e9201 100644 --- a/Source/Scene/SingleTileImageryProvider.js +++ b/Source/Scene/SingleTileImageryProvider.js @@ -7,7 +7,6 @@ define([ '../Core/DeveloperError', '../Core/Event', '../Core/GeographicTilingScheme', - '../Core/loadImage', '../Core/Rectangle', '../Core/Resource', '../Core/RuntimeError', @@ -22,7 +21,6 @@ define([ DeveloperError, Event, GeographicTilingScheme, - loadImage, Rectangle, Resource, RuntimeError, @@ -119,7 +117,7 @@ define([ } function doRequest() { - when(loadImage(resource), success, failure); + when(resource.fetchImage(), success, failure); } doRequest(); diff --git a/Source/Scene/TextureAtlas.js b/Source/Scene/TextureAtlas.js index 1cc37b2fff61..608be3d218f0 100644 --- a/Source/Scene/TextureAtlas.js +++ b/Source/Scene/TextureAtlas.js @@ -7,7 +7,6 @@ define([ '../Core/defineProperties', '../Core/destroyObject', '../Core/DeveloperError', - '../Core/loadImage', '../Core/PixelFormat', '../Core/Resource', '../Core/RuntimeError', @@ -23,7 +22,6 @@ define([ defineProperties, destroyObject, DeveloperError, - loadImage, PixelFormat, Resource, RuntimeError, @@ -359,11 +357,10 @@ define([ throw new DeveloperError('image is required.'); } //>>includeEnd('debug'); - } else if (typeof image === 'string') { - // if image is a string, load it as an image - image = loadImage(new Resource({url: image})); - } else if (image instanceof Resource) { - image = loadImage(image); + } else if ((typeof image === 'string') || (image instanceof Resource)) { + // Get a resource + var resource = Resource.createIfNeeded(image); + image = resource.fetchImage(); } var that = this; diff --git a/Source/Scene/UrlTemplateImageryProvider.js b/Source/Scene/UrlTemplateImageryProvider.js index d68d64436ba9..029a097934d3 100644 --- a/Source/Scene/UrlTemplateImageryProvider.js +++ b/Source/Scene/UrlTemplateImageryProvider.js @@ -13,10 +13,6 @@ define([ '../Core/Event', '../Core/GeographicTilingScheme', '../Core/isArray', - '../Core/loadJson', - '../Core/loadText', - '../Core/loadWithXhr', - '../Core/loadXML', '../Core/Math', '../Core/Rectangle', '../Core/Resource', @@ -38,10 +34,6 @@ define([ Event, GeographicTilingScheme, isArray, - loadJson, - loadText, - loadWithXhr, - loadXML, CesiumMath, Rectangle, Resource, @@ -727,14 +719,15 @@ define([ ++formatIndex; if (format.type === 'json') { - return loadJson(resource).then(format.callback).otherwise(doRequest); + return resource.fetchJson().then(format.callback).otherwise(doRequest); } else if (format.type === 'xml') { - return loadXML(resource).then(format.callback).otherwise(doRequest); + return resource.fetchXML().then(format.callback).otherwise(doRequest); } else if (format.type === 'text' || format.type === 'html') { - return loadText(resource).then(format.callback).otherwise(doRequest); + return resource.fetchText().then(format.callback).otherwise(doRequest); } - resource.responseType = format.format; - return loadWithXhr(resource).then(handleResponse.bind(undefined, format)).otherwise(doRequest); + return resource.fetch({ + responseType: format.format + }).then(handleResponse.bind(undefined, format)).otherwise(doRequest); } return doRequest(); diff --git a/Source/Scene/createTileMapServiceImageryProvider.js b/Source/Scene/createTileMapServiceImageryProvider.js index e1a2cc62dd61..cf1667f0211a 100644 --- a/Source/Scene/createTileMapServiceImageryProvider.js +++ b/Source/Scene/createTileMapServiceImageryProvider.js @@ -6,7 +6,6 @@ define([ '../Core/deprecationWarning', '../Core/DeveloperError', '../Core/GeographicTilingScheme', - '../Core/loadXML', '../Core/Rectangle', '../Core/Resource', '../Core/RuntimeError', @@ -22,7 +21,6 @@ define([ deprecationWarning, DeveloperError, GeographicTilingScheme, - loadXML, Rectangle, Resource, RuntimeError, @@ -281,7 +279,7 @@ define([ function requestMetadata() { // Try to load remaining parameters from XML - loadXML(xmlResource).then(metadataSuccess).otherwise(metadataFailure); + xmlResource.fetchXML().then(metadataSuccess).otherwise(metadataFailure); } requestMetadata(); diff --git a/Specs/Core/ResourceSpec.js b/Specs/Core/ResourceSpec.js index a6705b70a97f..ca4d9e455144 100644 --- a/Specs/Core/ResourceSpec.js +++ b/Specs/Core/ResourceSpec.js @@ -29,11 +29,6 @@ defineSuite([ headers: { Accept: 'application/test-type' }, - responseType: 'arraybuffer', - method: 'POST', - data: { - stuff: 'more stuff' - }, proxy: proxy, retryCallback: retryFunc, retryAttempts: 4, @@ -56,11 +51,6 @@ defineSuite([ expect(resource.headers).toEqual({ Accept: 'application/test-type' }); - expect(resource.responseType).toEqual('arraybuffer'); - expect(resource.method).toEqual('POST'); - expect(resource.data).toEqual({ - stuff: 'more stuff' - }); expect(resource.proxy).toBe(proxy); expect(resource.retryCallback).toBe(retryFunc); expect(resource.retryAttempts).toEqual(4); @@ -68,6 +58,19 @@ defineSuite([ expect(resource.request).toBe(request); }); + it('Constructor sets correct properties', function() { + var url = 'http://invalid.domain.com/tileset'; + var resource = new Resource(url); + expect(resource.url).toEqual(url); + expect(resource.queryParameters).toEqual({}); + expect(resource.templateValues).toEqual({}); + expect(resource.headers).toEqual({}); + expect(resource.proxy).toBeUndefined(); + expect(resource.retryCallback).toBeUndefined(); + expect(resource.retryAttempts).toEqual(0); + expect(resource.request).toBeDefined(); + }); + it('appendForwardSlash appends a /', function() { var resource = new Resource({ url: 'http://test.com/tileset' @@ -148,11 +151,6 @@ defineSuite([ headers: { Accept: 'application/test-type' }, - responseType: 'arraybuffer', - method: 'POST', - data: { - stuff: 'more stuff' - }, proxy: proxy, retryCallback: retryFunc, retryAttempts: 4, @@ -179,11 +177,6 @@ defineSuite([ expect(resource.headers).toEqual({ Accept: 'application/test-type' }); - expect(resource.responseType).toEqual('arraybuffer'); - expect(resource.method).toEqual('POST'); - expect(resource.data).toEqual({ - stuff: 'more stuff' - }); expect(resource.proxy).toBe(proxy); expect(resource.retryCallback).toBe(retryFunc); expect(resource.retryAttempts).toEqual(4); @@ -419,4 +412,181 @@ defineSuite([ expect(resource._retryCount).toEqual(4); }); }); + + it('isDataUri returns correct values', function() { + var dataResource = new Resource({ + url: 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3' + }); + + expect(dataResource.isDataUri).toBe(true); + + var resource = new Resource({ + url: 'http://invalid.uri/tileset' + }); + + expect(resource.isDataUri).toBe(false); + }); + + it('isBlobUri returns correct values', function() { + var dataResource = new Resource({ + url: 'blob:d3958f5c-0777-0845-9dcf-2cb28783acaf' + }); + + expect(dataResource.isBlobUri).toBe(true); + + var resource = new Resource({ + url: 'http://invalid.uri/tileset' + }); + + expect(resource.isBlobUri).toBe(false); + }); + + it('post calls with correct method', function() { + var expectedUrl = 'http://test.com/endpoint'; + var expectedResponseType = 'json'; + var expectedData = { + stuff: 'myStuff' + }; + var expectedHeaders = { + 'X-My-Header': 'My-Value' + }; + var expectedResult = { + status: 'success' + }; + var expectedMimeType = 'application/test-data'; + var resource = new Resource({ + url: expectedUrl, + headers: expectedHeaders + }); + + spyOn(Resource._Implementations, 'loadWithXhr').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + expect(url).toEqual(expectedUrl); + expect(responseType).toEqual(expectedResponseType); + expect(method).toEqual('POST'); + expect(data).toEqual(expectedData); + expect(headers['X-My-Header']).toEqual('My-Value'); + expect(headers['X-My-Other-Header']).toEqual('My-Other-Value'); + expect(overrideMimeType).toBe(expectedMimeType); + deferred.resolve(expectedResult); + }); + + return resource.post(expectedData, { + responseType: expectedResponseType, + headers: { + 'X-My-Other-Header': 'My-Other-Value' + }, + overrideMimeType: expectedMimeType + }) + .then(function(result) { + expect(result).toEqual(expectedResult); + }); + }); + + it('static post calls with correct method', function() { + var expectedUrl = 'http://test.com/endpoint'; + var expectedResponseType = 'json'; + var expectedData = { + stuff: 'myStuff' + }; + var expectedHeaders = { + 'X-My-Header': 'My-Value' + }; + var expectedResult = { + status: 'success' + }; + var expectedMimeType = 'application/test-data'; + + spyOn(Resource._Implementations, 'loadWithXhr').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + expect(url).toEqual(expectedUrl); + expect(responseType).toEqual(expectedResponseType); + expect(method).toEqual('POST'); + expect(data).toEqual(expectedData); + expect(headers).toEqual(expectedHeaders); + expect(overrideMimeType).toBe(expectedMimeType); + deferred.resolve(expectedResult); + }); + + return Resource.post({ + url: expectedUrl, + data: expectedData, + responseType: expectedResponseType, + headers: expectedHeaders, + overrideMimeType: expectedMimeType + }) + .then(function(result) { + expect(result).toEqual(expectedResult); + }); + }); + + it('static fetchArrayBuffer calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetchArrayBuffer').and.returnValue(when.resolve()); + return Resource.fetchArrayBuffer(url) + .then(function() { + expect(Resource.prototype.fetchArrayBuffer).toHaveBeenCalled(); + }); + }); + + it('static fetchBlob calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetchBlob').and.returnValue(when.resolve()); + return Resource.fetchBlob(url) + .then(function() { + expect(Resource.prototype.fetchBlob).toHaveBeenCalled(); + }); + }); + + it('static fetchImage calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetchImage').and.returnValue(when.resolve()); + return Resource.fetchImage(url) + .then(function() { + expect(Resource.prototype.fetchImage).toHaveBeenCalled(); + }); + }); + + it('static fetchText calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetchText').and.returnValue(when.resolve()); + return Resource.fetchText(url) + .then(function() { + expect(Resource.prototype.fetchText).toHaveBeenCalled(); + }); + }); + + it('static fetchJson calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetchJson').and.returnValue(when.resolve()); + return Resource.fetchJson(url) + .then(function() { + expect(Resource.prototype.fetchJson).toHaveBeenCalled(); + }); + }); + + it('static fetchXML calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetchXML').and.returnValue(when.resolve()); + return Resource.fetchXML(url) + .then(function() { + expect(Resource.prototype.fetchXML).toHaveBeenCalled(); + }); + }); + + it('static fetchJsonp calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetchJsonp').and.returnValue(when.resolve()); + return Resource.fetchJsonp(url) + .then(function() { + expect(Resource.prototype.fetchJsonp).toHaveBeenCalled(); + }); + }); + + it('static fetch calls correct method', function() { + var url = 'http://test.com/data'; + spyOn(Resource.prototype, 'fetch').and.returnValue(when.resolve()); + return Resource.fetch(url) + .then(function() { + expect(Resource.prototype.fetch).toHaveBeenCalled(); + }); + }); }); diff --git a/Specs/Core/loadImageSpec.js b/Specs/Core/loadImageSpec.js index 3bb900c29cb6..aa90aed8bfa0 100644 --- a/Specs/Core/loadImageSpec.js +++ b/Specs/Core/loadImageSpec.js @@ -1,11 +1,13 @@ defineSuite([ 'Core/loadImage', + 'Core/loadWithXhr', 'Core/Request', 'Core/RequestScheduler', 'Core/Resource', 'ThirdParty/when' ], function( loadImage, + loadWithXhr, Request, RequestScheduler, Resource, @@ -135,6 +137,63 @@ defineSuite([ RequestScheduler.maximumRequests = oldMaximumRequests; }); + it('Calls loadWithXhr with blob response type if headers is set', function() { + var expectedUrl = 'http://example.invalid/testuri.png'; + var expectedHeaders = { + 'X-my-header': 'my-value' + }; + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + expect(url).toEqual(expectedUrl); + expect(headers).toEqual(expectedHeaders); + expect(responseType).toEqual('blob'); + + var binary = atob(dataUri.split(',')[1]); + var array = []; + for(var i = 0; i < binary.length; i++) { + array.push(binary.charCodeAt(i)); + } + + deferred.resolve(new Blob([new Uint8Array(array)], {type: 'image/png'})); + }); + + var testResource = new Resource({ + url: expectedUrl, + headers: expectedHeaders + }); + var promise = loadImage(testResource); + expect(promise).toBeDefined(); + + return promise + .then(function(image) { + expect(image).toBeDefined(); + }); + }); + + it('Doesn\'t call loadWithXhr with blob response type if headers is set but is a data URI', function() { + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + deferred.reject('this shouldn\'t happen'); + }); + + spyOn(loadImage, 'createImage').and.callFake(function(url, crossOrigin, deferred) { + expect(url).toEqual(dataUri); + return loadImage.defaultCreateImage(url, crossOrigin, deferred); + }); + + var testResource = new Resource({ + url: dataUri, + headers: { + 'X-my-header': 'my-value' + } + }); + var promise = loadImage(testResource); + expect(promise).toBeDefined(); + + return promise + .then(function(image) { + expect(image).toBeDefined(); + }); + }); + describe('retries when Resource has the callback set', function() { it('rejects after too many retries', function() { var fakeImage = {};