; | |
module.exports = fetch; | |
var asPromise = require("@protobufjs/aspromise"), | |
inquire = require("@protobufjs/inquire"); | |
var fs = inquire("fs"); | |
/** | |
* Node-style callback as used by {@link util.fetch}. | |
* @typedef FetchCallback | |
* @type {function} | |
* @param {?Error} error Error, if any, otherwise `null` | |
* @param {string} [contents] File contents, if there hasn't been an error | |
* @returns {undefined} | |
*/ | |
/** | |
* Options as used by {@link util.fetch}. | |
* @typedef FetchOptions | |
* @type {Object} | |
* @property {boolean} [binary=false] Whether expecting a binary response | |
* @property {boolean} [xhr=false] If `true`, forces the use of XMLHttpRequest | |
*/ | |
/** | |
* Fetches the contents of a file. | |
* @memberof util | |
* @param {string} filename File path or url | |
* @param {FetchOptions} options Fetch options | |
* @param {FetchCallback} callback Callback function | |
* @returns {undefined} | |
*/ | |
function fetch(filename, options, callback) { | |
if (typeof options === "function") { | |
callback = options; | |
options = {}; | |
} else if (!options) | |
options = {}; | |
if (!callback) | |
return asPromise(fetch, this, filename, options); // eslint-disable-line no-invalid-this | |
// if a node-like filesystem is present, try it first but fall back to XHR if nothing is found. | |
if (!options.xhr && fs && fs.readFile) | |
return fs.readFile(filename, function fetchReadFileCallback(err, contents) { | |
return err && typeof XMLHttpRequest !== "undefined" | |
? fetch.xhr(filename, options, callback) | |
: err | |
? callback(err) | |
: callback(null, options.binary ? contents : contents.toString("utf8")); | |
}); | |
// use the XHR version otherwise. | |
return fetch.xhr(filename, options, callback); | |
} | |
/** | |
* Fetches the contents of a file. | |
* @name util.fetch | |
* @function | |
* @param {string} path File path or url | |
* @param {FetchCallback} callback Callback function | |
* @returns {undefined} | |
* @variation 2 | |
*/ | |
/** | |
* Fetches the contents of a file. | |
* @name util.fetch | |
* @function | |
* @param {string} path File path or url | |
* @param {FetchOptions} [options] Fetch options | |
* @returns {Promise<string|Uint8Array>} Promise | |
* @variation 3 | |
*/ | |
/**/ | |
fetch.xhr = function fetch_xhr(filename, options, callback) { | |
var xhr = new XMLHttpRequest(); | |
xhr.onreadystatechange /* works everywhere */ = function fetchOnReadyStateChange() { | |
if (xhr.readyState !== 4) | |
return undefined; | |
// local cors security errors return status 0 / empty string, too. afaik this cannot be | |
// reliably distinguished from an actually empty file for security reasons. feel free | |
// to send a pull request if you are aware of a solution. | |
if (xhr.status !== 0 && xhr.status !== 200) | |
return callback(Error("status " + xhr.status)); | |
// if binary data is expected, make sure that some sort of array is returned, even if | |
// ArrayBuffers are not supported. the binary string fallback, however, is unsafe. | |
if (options.binary) { | |
var buffer = xhr.response; | |
if (!buffer) { | |
buffer = []; | |
for (var i = 0; i < xhr.responseText.length; ++i) | |
buffer.push(xhr.responseText.charCodeAt(i) & 255); | |
} | |
return callback(null, typeof Uint8Array !== "undefined" ? new Uint8Array(buffer) : buffer); | |
} | |
return callback(null, xhr.responseText); | |
}; | |
if (options.binary) { | |
// ref: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data#Receiving_binary_data_in_older_browsers | |
if ("overrideMimeType" in xhr) | |
xhr.overrideMimeType("text/plain; charset=x-user-defined"); | |
xhr.responseType = "arraybuffer"; | |
} | |
xhr.open("GET", filename); | |
xhr.send(); | |
}; | |