Commit d29592ce authored by Romain Courteaud's avatar Romain Courteaud

Node: build a NodeJS compatible version.

Implement a basic FormData support, used to serialize the POST body.

Use xhr2 library to replicate XHMLHttpRequest.

Desactivate all broken storages (IndexedDB, localstorage, webdav, ...) which rely on browser API.

Thanks to Aurelien Calonne <aurel@nexedi.com> and Guillaume Royer <guillaume.royer@clearroad.io> for their contributions.
parent c9fc030f
...@@ -29,6 +29,8 @@ EXTERNALDIR = external ...@@ -29,6 +29,8 @@ EXTERNALDIR = external
VERSION = 3.32.1 VERSION = 3.32.1
JIOVERSION = ${DISTDIR}/jio-v${VERSION}.js JIOVERSION = ${DISTDIR}/jio-v${VERSION}.js
JIOLATEST = ${DISTDIR}/jio-latest.js JIOLATEST = ${DISTDIR}/jio-latest.js
JIONODEVERSION = ${DISTDIR}/jio-v${VERSION}-node.js
JIONODELATEST = ${DISTDIR}/jio-latest-node.js
all: fetch lint build all: fetch lint build
...@@ -77,6 +79,21 @@ ${LINTDIR}/${EXAMPLEDIR}/%.js: ${EXAMPLEDIR}/%.js ...@@ -77,6 +79,21 @@ ${LINTDIR}/${EXAMPLEDIR}/%.js: ${EXAMPLEDIR}/%.js
${JSLINT} ${LINTOPTS} --predef RSVP --predef window --predef QUnit --predef jIO --predef rJS $< ${JSLINT} ${LINTOPTS} --predef RSVP --predef window --predef QUnit --predef jIO --predef rJS $<
@cat $< > $@ @cat $< > $@
${LINTDIR}/node/%.js: ${SRCDIR}/node/%.js
@mkdir -p $(@D)
${JSLINT} ${LINTOPTS} --nomen true $<
@cat $< > $@
${LINTDIR}/${TESTDIR}/%.js: ${TESTDIR}/%.js
@mkdir -p $(@D)
${JSLINT} ${LINTOPTS} $<
@cat $< > $@
${LINTDIR}/${TESTDIR}/node/%.js: ${TESTDIR}/node/%.js
@mkdir -p $(@D)
${JSLINT} ${LINTOPTS} $<
@cat $< > $@
############################################# #############################################
# Check test files # Check test files
############################################# #############################################
...@@ -88,12 +105,15 @@ lint: $(patsubst ${TESTDIR}/jio.storage/%.js, ${LINTDIR}/${TESTDIR}/jio.storage/ ...@@ -88,12 +105,15 @@ lint: $(patsubst ${TESTDIR}/jio.storage/%.js, ${LINTDIR}/${TESTDIR}/jio.storage/
${LINTDIR}/queries/query.js \ ${LINTDIR}/queries/query.js \
${LINTDIR}/jio.date/jiodate.js \ ${LINTDIR}/jio.date/jiodate.js \
${LINTDIR}/jio.js \ ${LINTDIR}/jio.js \
${LINTDIR}/node/jio.js \
${LINTDIR}/${TESTDIR}/node.js \
${LINTDIR}/${TESTDIR}/node/node-require.js \
$(patsubst ${SRCDIR}/jio.storage/%.js, ${LINTDIR}/jio.storage/%.js, $(wildcard ${SRCDIR}/jio.storage/*.js)) $(patsubst ${SRCDIR}/jio.storage/%.js, ${LINTDIR}/jio.storage/%.js, $(wildcard ${SRCDIR}/jio.storage/*.js))
############################################# #############################################
# Build # Build
############################################# #############################################
build: ${JIOLATEST} build: ${JIOLATEST} ${JIONODELATEST}
${JIOLATEST}: ${JIOVERSION} ${JIOLATEST}: ${JIOVERSION}
@mkdir -p $(@D) @mkdir -p $(@D)
...@@ -134,6 +154,45 @@ ${JIOVERSION}: ${EXTERNALDIR}/URI.js \ ...@@ -134,6 +154,45 @@ ${JIOVERSION}: ${EXTERNALDIR}/URI.js \
@mkdir -p $(@D) @mkdir -p $(@D)
cat $^ > $@ cat $^ > $@
#############################################
# Node
#############################################
${JIONODELATEST}: ${JIONODEVERSION}
@mkdir -p $(@D)
cp $< $@
${JIONODEVERSION}: ${SRCDIR}/node/jio-start.js \
${EXTERNALDIR}/rsvp-2.0.4.js \
${EXTERNALDIR}/moment.js \
${EXTERNALDIR}/URI.js \
${EXTERNALDIR}/uritemplate.js \
${EXTERNALDIR}/rusha.js \
${SRCDIR}/node/jio-external.js \
${EXTERNALDIR}/xhr2.js \
${SRCDIR}/queries/parser-begin.js \
${SRCDIR}/queries/build/parser.js \
${SRCDIR}/queries/parser-end.js \
${SRCDIR}/queries/query.js \
${SRCDIR}/node/jio-compat.js \
${SRCDIR}/jio.date/jiodate.js \
${SRCDIR}/jio.js \
${SRCDIR}/node/jio.js \
${SRCDIR}/jio.storage/replicatestorage.js \
${SRCDIR}/jio.storage/shastorage.js \
${SRCDIR}/jio.storage/uuidstorage.js \
${SRCDIR}/jio.storage/memorystorage.js \
${SRCDIR}/jio.storage/dropboxstorage.js \
${SRCDIR}/jio.storage/gdrivestorage.js \
${SRCDIR}/jio.storage/unionstorage.js \
${SRCDIR}/jio.storage/erp5storage.js \
${SRCDIR}/jio.storage/querystorage.js \
${SRCDIR}/jio.storage/drivetojiomapping.js \
${SRCDIR}/jio.storage/documentstorage.js \
${SRCDIR}/jio.storage/fbstorage.js \
${SRCDIR}/node/jio-end.js
@mkdir -p $(@D)
cat $^ > $@
############################################# #############################################
# Jison # Jison
############################################# #############################################
...@@ -190,7 +249,7 @@ ${EXTERNALDIR}/renderjs-latest.js: ...@@ -190,7 +249,7 @@ ${EXTERNALDIR}/renderjs-latest.js:
@mkdir -p $(@D) @mkdir -p $(@D)
curl -s -o $@ https://lab.nexedi.com/nexedi/renderjs/raw/master/dist/renderjs-latest.js curl -s -o $@ https://lab.nexedi.com/nexedi/renderjs/raw/master/dist/renderjs-latest.js
.PHONY: clean ${JIOVERSION} .PHONY: clean ${JIOVERSION} ${JIONODEVERSION}
clean: clean:
rm -rf ${LINTDIR} rm -rf ${LINTDIR}
......
...@@ -9,7 +9,7 @@ The documentation can be found on [https://jio.nexedi.com/](https://jio.nexedi.c ...@@ -9,7 +9,7 @@ The documentation can be found on [https://jio.nexedi.com/](https://jio.nexedi.c
### jIO Quickstart ### jIO Quickstart
git clone https://lab.nexedi.com/nexedi/jio.git git clone https://lab.nexedi.com/nexedi/jio.git
cd jio.git cd jio.git
npm install jslint@0.9.2 jison@0.4.16 npm install jslint@0.9.2 jison@0.4.16 git://github.com/qunitjs/node-qunit.git#v0.9.3 sinon@1.7.3
make make
......
// Generated by CoffeeScript 1.12.2
(function() {
var InvalidStateError, NetworkError, ProgressEvent, SecurityError, SyntaxError, XMLHttpRequest, XMLHttpRequestEventTarget, XMLHttpRequestUpload, http, https, os, url,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
XMLHttpRequestEventTarget = (function() {
function XMLHttpRequestEventTarget() {
this.onloadstart = null;
this.onprogress = null;
this.onabort = null;
this.onerror = null;
this.onload = null;
this.ontimeout = null;
this.onloadend = null;
this._listeners = {};
}
XMLHttpRequestEventTarget.prototype.onloadstart = null;
XMLHttpRequestEventTarget.prototype.onprogress = null;
XMLHttpRequestEventTarget.prototype.onabort = null;
XMLHttpRequestEventTarget.prototype.onerror = null;
XMLHttpRequestEventTarget.prototype.onload = null;
XMLHttpRequestEventTarget.prototype.ontimeout = null;
XMLHttpRequestEventTarget.prototype.onloadend = null;
XMLHttpRequestEventTarget.prototype.addEventListener = function(eventType, listener) {
var base;
eventType = eventType.toLowerCase();
(base = this._listeners)[eventType] || (base[eventType] = []);
this._listeners[eventType].push(listener);
return void 0;
};
XMLHttpRequestEventTarget.prototype.removeEventListener = function(eventType, listener) {
var index;
eventType = eventType.toLowerCase();
if (this._listeners[eventType]) {
index = this._listeners[eventType].indexOf(listener);
if (index !== -1) {
this._listeners[eventType].splice(index, 1);
}
}
return void 0;
};
XMLHttpRequestEventTarget.prototype.dispatchEvent = function(event) {
var eventType, j, len, listener, listeners;
event.currentTarget = event.target = this;
eventType = event.type;
if (listeners = this._listeners[eventType]) {
for (j = 0, len = listeners.length; j < len; j++) {
listener = listeners[j];
listener.call(this, event);
}
}
if (listener = this["on" + eventType]) {
listener.call(this, event);
}
return void 0;
};
return XMLHttpRequestEventTarget;
})();
http = require('http');
https = require('https');
os = require('os');
url = require('url');
XMLHttpRequest = (function(superClass) {
extend(XMLHttpRequest, superClass);
function XMLHttpRequest(options) {
XMLHttpRequest.__super__.constructor.call(this);
this.onreadystatechange = null;
this._anonymous = options && options.anon;
this.readyState = XMLHttpRequest.UNSENT;
this.response = null;
this.responseText = '';
this.responseType = '';
this.responseURL = '';
this.status = 0;
this.statusText = '';
this.timeout = 0;
this.upload = new XMLHttpRequestUpload(this);
this._method = null;
this._url = null;
this._sync = false;
this._headers = null;
this._loweredHeaders = null;
this._mimeOverride = null;
this._request = null;
this._response = null;
this._responseParts = null;
this._responseHeaders = null;
this._aborting = null;
this._error = null;
this._loadedBytes = 0;
this._totalBytes = 0;
this._lengthComputable = false;
}
XMLHttpRequest.prototype.onreadystatechange = null;
XMLHttpRequest.prototype.readyState = null;
XMLHttpRequest.prototype.response = null;
XMLHttpRequest.prototype.responseText = null;
XMLHttpRequest.prototype.responseType = null;
XMLHttpRequest.prototype.status = null;
XMLHttpRequest.prototype.timeout = null;
XMLHttpRequest.prototype.upload = null;
XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
var xhrUrl;
method = method.toUpperCase();
if (method in this._restrictedMethods) {
throw new SecurityError("HTTP method " + method + " is not allowed in XHR");
}
xhrUrl = this._parseUrl(url);
if (async === void 0) {
async = true;
}
switch (this.readyState) {
case XMLHttpRequest.UNSENT:
case XMLHttpRequest.OPENED:
case XMLHttpRequest.DONE:
null;
break;
case XMLHttpRequest.HEADERS_RECEIVED:
case XMLHttpRequest.LOADING:
null;
}
this._method = method;
this._url = xhrUrl;
this._sync = !async;
this._headers = {};
this._loweredHeaders = {};
this._mimeOverride = null;
this._setReadyState(XMLHttpRequest.OPENED);
this._request = null;
this._response = null;
this.status = 0;
this.statusText = '';
this._responseParts = [];
this._responseHeaders = null;
this._loadedBytes = 0;
this._totalBytes = 0;
this._lengthComputable = false;
return void 0;
};
XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
var loweredName;
if (this.readyState !== XMLHttpRequest.OPENED) {
throw new InvalidStateError("XHR readyState must be OPENED");
}
loweredName = name.toLowerCase();
if (this._restrictedHeaders[loweredName] || /^sec\-/.test(loweredName) || /^proxy-/.test(loweredName)) {
console.warn("Refused to set unsafe header \"" + name + "\"");
return void 0;
}
value = value.toString();
if (loweredName in this._loweredHeaders) {
name = this._loweredHeaders[loweredName];
this._headers[name] = this._headers[name] + ', ' + value;
} else {
this._loweredHeaders[loweredName] = name;
this._headers[name] = value;
}
return void 0;
};
XMLHttpRequest.prototype.send = function(data) {
if (this.readyState !== XMLHttpRequest.OPENED) {
throw new InvalidStateError("XHR readyState must be OPENED");
}
if (this._request) {
throw new InvalidStateError("send() already called");
}
switch (this._url.protocol) {
case 'file:':
this._sendFile(data);
break;
case 'http:':
case 'https:':
this._sendHttp(data);
break;
default:
throw new NetworkError("Unsupported protocol " + this._url.protocol);
}
return void 0;
};
XMLHttpRequest.prototype.abort = function() {
if (!this._request) {
return;
}
this._request.abort();
this._setError();
this._dispatchProgress('abort');
this._dispatchProgress('loadend');
return void 0;
};
XMLHttpRequest.prototype.getResponseHeader = function(name) {
var loweredName;
if (!this._responseHeaders) {
return null;
}
loweredName = name.toLowerCase();
if (loweredName in this._responseHeaders) {
return this._responseHeaders[loweredName];
} else {
return null;
}
};
XMLHttpRequest.prototype.getAllResponseHeaders = function() {
var lines, name, value;
if (!this._responseHeaders) {
return '';
}
lines = (function() {
var ref, results;
ref = this._responseHeaders;
results = [];
for (name in ref) {
value = ref[name];
results.push(name + ": " + value);
}
return results;
}).call(this);
return lines.join("\r\n");
};
XMLHttpRequest.prototype.overrideMimeType = function(newMimeType) {
if (this.readyState === XMLHttpRequest.LOADING || this.readyState === XMLHttpRequest.DONE) {
throw new InvalidStateError("overrideMimeType() not allowed in LOADING or DONE");
}
this._mimeOverride = newMimeType.toLowerCase();
return void 0;
};
XMLHttpRequest.prototype.nodejsSet = function(options) {
var baseUrl, parsedUrl;
if ('httpAgent' in options) {
this.nodejsHttpAgent = options.httpAgent;
}
if ('httpsAgent' in options) {
this.nodejsHttpsAgent = options.httpsAgent;
}
if ('baseUrl' in options) {
baseUrl = options.baseUrl;
if (baseUrl !== null) {
parsedUrl = url.parse(baseUrl, false, true);
if (!parsedUrl.protocol) {
throw new SyntaxError("baseUrl must be an absolute URL");
}
}
this.nodejsBaseUrl = baseUrl;
}
return void 0;
};
XMLHttpRequest.nodejsSet = function(options) {
XMLHttpRequest.prototype.nodejsSet(options);
return void 0;
};
XMLHttpRequest.prototype.UNSENT = 0;
XMLHttpRequest.UNSENT = 0;
XMLHttpRequest.prototype.OPENED = 1;
XMLHttpRequest.OPENED = 1;
XMLHttpRequest.prototype.HEADERS_RECEIVED = 2;
XMLHttpRequest.HEADERS_RECEIVED = 2;
XMLHttpRequest.prototype.LOADING = 3;
XMLHttpRequest.LOADING = 3;
XMLHttpRequest.prototype.DONE = 4;
XMLHttpRequest.DONE = 4;
XMLHttpRequest.prototype.nodejsHttpAgent = http.globalAgent;
XMLHttpRequest.prototype.nodejsHttpsAgent = https.globalAgent;
XMLHttpRequest.prototype.nodejsBaseUrl = null;
XMLHttpRequest.prototype._restrictedMethods = {
CONNECT: true,
TRACE: true,
TRACK: true
};
XMLHttpRequest.prototype._restrictedHeaders = {
'accept-charset': true,
'accept-encoding': true,
'access-control-request-headers': true,
'access-control-request-method': true,
connection: true,
'content-length': true,
// cookie: true,
cookie2: true,
date: true,
dnt: true,
expect: true,
host: true,
'keep-alive': true,
origin: true,
referer: true,
te: true,
trailer: true,
'transfer-encoding': true,
upgrade: true,
'user-agent': true,
via: true
};
XMLHttpRequest.prototype._privateHeaders = {
// 'set-cookie': true,
'set-cookie2': true
};
XMLHttpRequest.prototype._userAgent = ("Mozilla/5.0 (" + (os.type()) + " " + (os.arch()) + ") ") + ("node.js/" + process.versions.node + " v8/" + process.versions.v8);
XMLHttpRequest.prototype._setReadyState = function(newReadyState) {
var event;
this.readyState = newReadyState;
event = new ProgressEvent('readystatechange');
this.dispatchEvent(event);
return void 0;
};
XMLHttpRequest.prototype._sendFile = function() {
if (this._url.method !== 'GET') {
throw new NetworkError('The file protocol only supports GET');
}
throw new Error("Protocol file: not implemented");
};
XMLHttpRequest.prototype._sendHttp = function(data) {
if (this._sync) {
throw new Error("Synchronous XHR processing not implemented");
}
if ((data != null) && (this._method === 'GET' || this._method === 'HEAD')) {
console.warn("Discarding entity body for " + this._method + " requests");
data = null;
} else {
data || (data = '');
}
this.upload._setData(data);
this._finalizeHeaders();
this._sendHxxpRequest();
return void 0;
};
XMLHttpRequest.prototype._sendHxxpRequest = function() {
var agent, hxxp, request;
if (this._url.protocol === 'http:') {
hxxp = http;
agent = this.nodejsHttpAgent;
} else {
hxxp = https;
agent = this.nodejsHttpsAgent;
}
request = hxxp.request({
hostname: this._url.hostname,
port: this._url.port,
path: this._url.path,
auth: this._url.auth,
method: this._method,
headers: this._headers,
agent: agent
});
this._request = request;
if (this.timeout) {
request.setTimeout(this.timeout, (function(_this) {
return function() {
return _this._onHttpTimeout(request);
};
})(this));
}
request.on('response', (function(_this) {
return function(response) {
return _this._onHttpResponse(request, response);
};
})(this));
request.on('error', (function(_this) {
return function(error) {
return _this._onHttpRequestError(request, error);
};
})(this));
this.upload._startUpload(request);
if (this._request === request) {
this._dispatchProgress('loadstart');
}
return void 0;
};
XMLHttpRequest.prototype._finalizeHeaders = function() {
this._headers['Connection'] = 'keep-alive';
this._headers['Host'] = this._url.host;
if (this._anonymous) {
this._headers['Referer'] = 'about:blank';
}
this._headers['User-Agent'] = this._userAgent;
this.upload._finalizeHeaders(this._headers, this._loweredHeaders);
return void 0;
};
XMLHttpRequest.prototype._onHttpResponse = function(request, response) {
var lengthString;
if (this._request !== request) {
return;
}
switch (response.statusCode) {
case 301:
case 302:
case 303:
case 307:
case 308:
this._url = this._parseUrl(response.headers['location']);
this._method = 'GET';
if ('content-type' in this._loweredHeaders) {
delete this._headers[this._loweredHeaders['content-type']];
delete this._loweredHeaders['content-type'];
}
if ('Content-Type' in this._headers) {
delete this._headers['Content-Type'];
}
delete this._headers['Content-Length'];
this.upload._reset();
this._finalizeHeaders();
this._sendHxxpRequest();
return;
}
this._response = response;
this._response.on('data', (function(_this) {
return function(data) {
return _this._onHttpResponseData(response, data);
};
})(this));
this._response.on('end', (function(_this) {
return function() {
return _this._onHttpResponseEnd(response);
};
})(this));
this._response.on('close', (function(_this) {
return function() {
return _this._onHttpResponseClose(response);
};
})(this));
this.responseURL = this._url.href.split('#')[0];
this.status = this._response.statusCode;
this.statusText = http.STATUS_CODES[this.status];
this._parseResponseHeaders(response);
if (lengthString = this._responseHeaders['content-length']) {
this._totalBytes = parseInt(lengthString);
this._lengthComputable = true;
} else {
this._lengthComputable = false;
}
return this._setReadyState(XMLHttpRequest.HEADERS_RECEIVED);
};
XMLHttpRequest.prototype._onHttpResponseData = function(response, data) {
if (this._response !== response) {
return;
}
this._responseParts.push(data);
this._loadedBytes += data.length;
if (this.readyState !== XMLHttpRequest.LOADING) {
this._setReadyState(XMLHttpRequest.LOADING);
}
return this._dispatchProgress('progress');
};
XMLHttpRequest.prototype._onHttpResponseEnd = function(response) {
if (this._response !== response) {
return;
}
this._parseResponse();
this._request = null;
this._response = null;
this._setReadyState(XMLHttpRequest.DONE);
this._dispatchProgress('load');
return this._dispatchProgress('loadend');
};
XMLHttpRequest.prototype._onHttpResponseClose = function(response) {
var request;
if (this._response !== response) {
return;
}
request = this._request;
this._setError();
request.abort();
this._setReadyState(XMLHttpRequest.DONE);
this._dispatchProgress('error');
return this._dispatchProgress('loadend');
};
XMLHttpRequest.prototype._onHttpTimeout = function(request) {
if (this._request !== request) {
return;
}
this._setError();
request.abort();
this._setReadyState(XMLHttpRequest.DONE);
this._dispatchProgress('timeout');
return this._dispatchProgress('loadend');
};
XMLHttpRequest.prototype._onHttpRequestError = function(request, error) {
if (this._request !== request) {
return;
}
this._setError();
request.abort();
this._setReadyState(XMLHttpRequest.DONE);
this._dispatchProgress('error');
return this._dispatchProgress('loadend');
};
XMLHttpRequest.prototype._dispatchProgress = function(eventType) {
var event;
event = new ProgressEvent(eventType);
event.lengthComputable = this._lengthComputable;
event.loaded = this._loadedBytes;
event.total = this._totalBytes;
this.dispatchEvent(event);
return void 0;
};
XMLHttpRequest.prototype._setError = function() {
this._request = null;
this._response = null;
this._responseHeaders = null;
this._responseParts = null;
return void 0;
};
XMLHttpRequest.prototype._parseUrl = function(urlString) {
var absoluteUrlString, index, password, user, xhrUrl;
if (this.nodejsBaseUrl === null) {
absoluteUrlString = urlString;
} else {
absoluteUrlString = url.resolve(this.nodejsBaseUrl, urlString);
}
xhrUrl = url.parse(absoluteUrlString, false, true);
xhrUrl.hash = null;
if (xhrUrl.auth && ((typeof user !== "undefined" && user !== null) || (typeof password !== "undefined" && password !== null))) {
index = xhrUrl.auth.indexOf(':');
if (index === -1) {
if (!user) {
user = xhrUrl.auth;
}
} else {
if (!user) {
user = xhrUrl.substring(0, index);
}
if (!password) {
password = xhrUrl.substring(index + 1);
}
}
}
if (user || password) {
xhrUrl.auth = user + ":" + password;
}
return xhrUrl;
};
XMLHttpRequest.prototype._parseResponseHeaders = function(response) {
var loweredName, name, ref, value;
this._responseHeaders = {};
ref = response.headers;
for (name in ref) {
value = ref[name];
loweredName = name.toLowerCase();
if (this._privateHeaders[loweredName]) {
continue;
}
if (this._mimeOverride !== null && loweredName === 'content-type') {
value = this._mimeOverride;
}
this._responseHeaders[loweredName] = value;
}
if (this._mimeOverride !== null && !('content-type' in this._responseHeaders)) {
this._responseHeaders['content-type'] = this._mimeOverride;
}
return void 0;
};
XMLHttpRequest.prototype._parseResponse = function() {
var arrayBuffer, buffer, i, j, jsonError, ref, view;
if (Buffer.concat) {
buffer = Buffer.concat(this._responseParts);
} else {
buffer = this._concatBuffers(this._responseParts);
}
this._responseParts = null;
switch (this.responseType) {
case 'text':
this._parseTextResponse(buffer);
break;
case 'json':
this.responseText = null;
try {
this.response = JSON.parse(buffer.toString('utf-8'));
} catch (error1) {
jsonError = error1;
this.response = null;
}
break;
case 'buffer':
this.responseText = null;
this.response = buffer;
break;
case 'arraybuffer':
this.responseText = null;
arrayBuffer = new ArrayBuffer(buffer.length);
view = new Uint8Array(arrayBuffer);
for (i = j = 0, ref = buffer.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
view[i] = buffer[i];
}
this.response = arrayBuffer;
break;
default:
this._parseTextResponse(buffer);
}
return void 0;
};
XMLHttpRequest.prototype._parseTextResponse = function(buffer) {
var e;
try {
this.responseText = buffer.toString(this._parseResponseEncoding());
} catch (error1) {
e = error1;
this.responseText = buffer.toString('binary');
}
this.response = this.responseText;
return void 0;
};
XMLHttpRequest.prototype._parseResponseEncoding = function() {
var contentType, encoding, match;
encoding = null;
if (contentType = this._responseHeaders['content-type']) {
if (match = /\;\s*charset\=(.*)$/.exec(contentType)) {
return match[1];
}
}
return 'utf-8';
};
XMLHttpRequest.prototype._concatBuffers = function(buffers) {
var buffer, j, k, len, len1, length, target;
if (buffers.length === 0) {
return new Buffer(0);
}
if (buffers.length === 1) {
return buffers[0];
}
length = 0;
for (j = 0, len = buffers.length; j < len; j++) {
buffer = buffers[j];
length += buffer.length;
}
target = new Buffer(length);
length = 0;
for (k = 0, len1 = buffers.length; k < len1; k++) {
buffer = buffers[k];
buffer.copy(target, length);
length += buffer.length;
}
return target;
};
return XMLHttpRequest;
})(XMLHttpRequestEventTarget);
module.exports = XMLHttpRequest;
XMLHttpRequest.XMLHttpRequest = XMLHttpRequest;
SecurityError = (function(superClass) {
extend(SecurityError, superClass);
function SecurityError() {
SecurityError.__super__.constructor.apply(this, arguments);
}
return SecurityError;
})(Error);
XMLHttpRequest.SecurityError = SecurityError;
InvalidStateError = (function(superClass) {
extend(InvalidStateError, superClass);
function InvalidStateError() {
InvalidStateError.__super__.constructor.apply(this, arguments);
}
return InvalidStateError;
})(Error);
InvalidStateError = (function(superClass) {
extend(InvalidStateError, superClass);
function InvalidStateError() {
return InvalidStateError.__super__.constructor.apply(this, arguments);
}
return InvalidStateError;
})(Error);
XMLHttpRequest.InvalidStateError = InvalidStateError;
NetworkError = (function(superClass) {
extend(NetworkError, superClass);
function NetworkError() {
NetworkError.__super__.constructor.apply(this, arguments);
}
return NetworkError;
})(Error);
XMLHttpRequest.SyntaxError = SyntaxError;
SyntaxError = (function(superClass) {
extend(SyntaxError, superClass);
function SyntaxError() {
SyntaxError.__super__.constructor.apply(this, arguments);
}
return SyntaxError;
})(Error);
ProgressEvent = (function() {
function ProgressEvent(type) {
this.type = type;
this.target = null;
this.currentTarget = null;
this.lengthComputable = false;
this.loaded = 0;
this.total = 0;
}
ProgressEvent.prototype.bubbles = false;
ProgressEvent.prototype.cancelable = false;
ProgressEvent.prototype.target = null;
ProgressEvent.prototype.loaded = null;
ProgressEvent.prototype.lengthComputable = null;
ProgressEvent.prototype.total = null;
return ProgressEvent;
})();
XMLHttpRequest.ProgressEvent = ProgressEvent;
XMLHttpRequestUpload = (function(superClass) {
extend(XMLHttpRequestUpload, superClass);
function XMLHttpRequestUpload(request) {
XMLHttpRequestUpload.__super__.constructor.call(this);
this._request = request;
this._reset();
}
XMLHttpRequestUpload.prototype._reset = function() {
this._contentType = null;
this._body = null;
return void 0;
};
XMLHttpRequestUpload.prototype._setData = function(data) {
var body, i, j, k, offset, ref, ref1, view;
if (typeof data === 'undefined' || data === null) {
return;
}
if (typeof data === 'string') {
if (data.length !== 0) {
this._contentType = 'text/plain;charset=UTF-8';
}
this._body = new Buffer(data, 'utf8');
} else if (Buffer.isBuffer(data)) {
this._body = data;
} else if (data instanceof ArrayBuffer) {
body = new Buffer(data.byteLength);
view = new Uint8Array(data);
for (i = j = 0, ref = data.byteLength; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
body[i] = view[i];
}
this._body = body;
} else if (data.buffer && data.buffer instanceof ArrayBuffer) {
body = new Buffer(data.byteLength);
offset = data.byteOffset;
view = new Uint8Array(data.buffer);
for (i = k = 0, ref1 = data.byteLength; 0 <= ref1 ? k < ref1 : k > ref1; i = 0 <= ref1 ? ++k : --k) {
body[i] = view[i + offset];
}
this._body = body;
} else {
throw new Error("Unsupported send() data " + data);
}
return void 0;
};
XMLHttpRequestUpload.prototype._finalizeHeaders = function(headers, loweredHeaders) {
if (this._contentType) {
if (!('content-type' in loweredHeaders)) {
headers['Content-Type'] = this._contentType;
}
}
if (this._body) {
headers['Content-Length'] = this._body.length.toString();
}
return void 0;
};
XMLHttpRequestUpload.prototype._startUpload = function(request) {
if (this._body) {
request.write(this._body);
}
request.end();
return void 0;
};
return XMLHttpRequestUpload;
})(XMLHttpRequestEventTarget);
XMLHttpRequest.XMLHttpRequestUpload = XMLHttpRequestUpload;
}).call(this);
\ No newline at end of file
/*
* Copyright 2018, Nexedi SA
*
* This program is free software: you can Use, Study, Modify and Redistribute
* it under the terms of the GNU General Public License version 3, or (at your
* option) any later version, as published by the Free Software Foundation.
*
* You can also Link and Combine this program with other software covered by
* the terms of any of the Free Software licenses or any of the Open Source
* Initiative approved licenses and Convey the resulting work. Corresponding
* source of such a combination shall include the source code for all other
* software used.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See COPYING file for full licensing terms.
* See https://www.nexedi.com/licensing for rationale and options.
*/
/*global window, WeakMap, ArrayBuffer, Uint8Array */
(function (window, WeakMap, ArrayBuffer, Uint8Array) {
"use strict";
var html5weakmap = new WeakMap();
function EventTarget() {
html5weakmap.set(this, Object.create(null));
}
EventTarget.prototype.addEventListener = function (type, listener) {
if (typeof listener !== 'function') {
return;
}
var em = html5weakmap.get(this);
type = type.toString();
if (em[type]) {
em[type].push(listener);
} else {
em[type] = [listener];
}
};
EventTarget.prototype.removeEventListener = function (type, listener) {
if (typeof listener !== 'function') {
return;
}
var em = html5weakmap.get(this),
i,
listeners = em[type];
type = type.toString();
if (listeners) {
for (i = 0; i < listeners.length; i += 1) {
if (listeners[i] === listener) {
if (listeners.length === 1) {
delete em[type];
return;
}
listeners.splice(i, 1);
return;
}
}
}
};
EventTarget.prototype.dispatchEvent = function (event) {
var type = event.type.toString(),
em = html5weakmap.get(this),
ontype = 'on' + type,
i,
listeners;
if (typeof this[ontype] === 'function') {
try {
this[ontype](event);
} catch (ignore) {}
}
listeners = em[type];
if (listeners) {
for (i = 0; i < listeners.length; i += 1) {
try {
listeners[i](event);
} catch (ignore) {}
}
}
};
window.EventTarget = window.EventTarget || EventTarget;
function Blob(blobParts, options) {
// https://developer.mozilla.org/en-US/docs/Web/API/Blob
var i,
priv = {},
buffers = [];
html5weakmap.set(this, priv);
if (blobParts) {
for (i = 0; i < blobParts.length; i += 1) {
if (Buffer.isBuffer(blobParts[i])) {
buffers.push(blobParts[i]);
} else if (blobParts[i] instanceof Blob) {
buffers.push(html5weakmap.get(blobParts[i]).data);
} else if (blobParts[i] instanceof ArrayBuffer) {
buffers.push(new Buffer(new Uint8Array(blobParts[i])));
} else {
buffers.push(new Buffer(String(blobParts[i])));
}
}
}
priv.data = Buffer.concat(buffers);
Object.defineProperty(this, 'size', {
enumerable: true,
value: priv.data.length
});
Object.defineProperty(this, 'type', {
enumerable: true,
value: options ? String(options.type || '') : ''
});
}
Blob.prototype.size = 0;
Blob.prototype.type = '';
Blob.prototype.slice = function (start, end, contentType) {
return new Blob([html5weakmap.get(this).data.slice(start, end)], {
type: contentType
});
};
window.Blob = Blob;//window.Blob || Blob;
function FileReader() {
EventTarget.call(this);
}
FileReader.prototype = Object.create(EventTarget.prototype);
Object.defineProperty(FileReader, 'constructor', {
value: FileReader
});
FileReader.prototype.readAsText = function (blob) {
var target = this,
priv = html5weakmap.get(blob),
result = priv.data.toString(),
event = Object.freeze({
type: 'load',
target: target
});
process.nextTick(function () {
target.result = result;
target.dispatchEvent(event);
});
};
FileReader.prototype.readAsArrayBuffer = function (blob) {
var target = this,
priv = html5weakmap.get(blob),
result = new Uint8Array(priv.data).buffer,
event = Object.freeze({
type: 'load',
target: target
});
process.nextTick(function () {
target.result = result;
target.dispatchEvent(event);
});
};
FileReader.prototype.readAsDataURL = function (blob) {
var target = this,
priv = html5weakmap.get(blob),
result = 'data:' + blob.type + ';base64,' + priv.data.toString('base64'),
event = Object.freeze({
type: 'load',
target: target
});
process.nextTick(function () {
target.result = result;
target.dispatchEvent(event);
});
};
window.FileReader = window.FileReader || FileReader;
function atob(str) {
try {
return window.atob(str);
} catch (err) {
var buffer;
if (str instanceof Buffer) {
buffer = str;
} else {
buffer = Buffer.from(str.toString(), 'base64');
}
return buffer.toString('binary');
}
}
window.atob = window.atob || atob;
function btoa(str) {
try {
return window.btoa(str);
} catch (err) {
var buffer;
if (str instanceof Buffer) {
buffer = str;
} else {
buffer = Buffer.from(str.toString(), 'binary');
}
return buffer.toString('base64');
}
}
window.btoa = window.btoa || btoa;
}(window, WeakMap, ArrayBuffer, Uint8Array));
var XMLHttpRequest = global.XMLHttpRequest || module.exports,
Blob = window.Blob,
atob = window.atob,
btoa = window.btoa,
FileReader = window.FileReader,
QueryFactory = window.QueryFactory,
Query = window.Query,
SimpleQuery = window.SimpleQuery,
ComplexQuery = window.ComplexQuery;
module = node_module;
jIO.node_env = window;
module.exports = jIO;
} ({}));
\ No newline at end of file
var RSVP = window.RSVP,
moment = global.moment,
UriTemplate = window.UriTemplate,
Rusha = window.Rusha;
// Allow xhr2 to export XMLHttpRequest
module = {};
(function (define, exports) {
var navigator = null,
window = {},
node_module = module;
module = undefined;
/*
* Copyright 2018, Nexedi SA
*
* This program is free software: you can Use, Study, Modify and Redistribute
* it under the terms of the GNU General Public License version 3, or (at your
* option) any later version, as published by the Free Software Foundation.
*
* You can also Link and Combine this program with other software covered by
* the terms of any of the Free Software licenses or any of the Open Source
* Initiative approved licenses and Convey the resulting work. Corresponding
* source of such a combination shall include the source code for all other
* software used.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See COPYING file for full licensing terms.
* See https://www.nexedi.com/licensing for rationale and options.
*/
/*global window */
(function (window, jIO, Blob) {
"use strict";
var FormData,
originalAjax;
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/
// Using_XMLHttpRequest#Submitting_forms_and_uploading_files
FormData = function FormData() {
this.boundary = "---------------------------" + Date.now().toString(16);
this.body = '';
};
FormData.prototype.append = function (name, value, filename) {
this.body += '--' + this.boundary +
'\r\nContent-Disposition: form-data; name="' + name;
if (filename !== undefined) {
this.body += '"; filename="' + filename;
}
this.body += '"\r\n\r\n' + value + '\r\n';
};
window.FormData = FormData;
originalAjax = jIO.util.ajax;
jIO.util.ajax = function ajax(param) {
if (param.data instanceof Blob) {
// Blob is not supported by xhr2, so convert to ArrayBuffer instead
return jIO.util.readBlobAsArrayBuffer(param.data).then(function (data) {
param.data = data.target.result;
return originalAjax(param);
});
}
if (param.data instanceof FormData) {
// Implement minimal FormData for erp5storage
if (!param.hasOwnProperty('headers')) {
param.headers = {};
}
param.headers["Content-Type"] = "multipart\/form-data; boundary=" +
param.data.boundary;
param.data.body += '--' + param.data.boundary + '--\r\n';
param.data = param.data.body;
return originalAjax(param);
}
return originalAjax(param);
};
}(window, window.jIO, window.Blob));
// Define a global variable to allow storages to access jIO
var jIO = window.jIO,
FormData = window.FormData,
jiodate = window.jiodate;
/*
* Copyright 2018, Nexedi SA
*
* This program is free software: you can Use, Study, Modify and Redistribute
* it under the terms of the GNU General Public License version 3, or (at your
* option) any later version, as published by the Free Software Foundation.
*
* You can also Link and Combine this program with other software covered by
* the terms of any of the Free Software licenses or any of the Open Source
* Initiative approved licenses and Convey the resulting work. Corresponding
* source of such a combination shall include the source code for all other
* software used.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See COPYING file for full licensing terms.
* See https://www.nexedi.com/licensing for rationale and options.
*/
/*global require, process, console, JSON */
(function (require, process, console, JSON) {
"use strict";
var testrunner = require('qunit'),
report_data = process.env.CI ? {} : null,
runner_options = process.env.CI ? {
log: {}
} : {
log: {
errors: true,
summary: true,
tests: true
}
};
testrunner.setup(runner_options);
if (report_data) {
testrunner.log.add = function (type, obj) {
if (!report_data.hasOwnProperty(type)) {
report_data[type] = [];
}
report_data[type].push(obj);
return report_data[type];
};
}
testrunner.run({
code: 'test/node/node-require.js',
tests: [
'test/jio/util.js',
'test/queries/key.tests.js',
'test/queries/key-schema.tests.js',
/*
'test/queries/tests.js',
*/
'test/queries/key-typechecks.tests.js',
'test/queries/jiodate.tests.js',
'test/queries/key-jiodate.tests.js',
'test/jio.storage/documentstorage.tests.js',
'test/jio.storage/drivetojiomapping.tests.js',
'test/jio.storage/dropboxstorage.tests.js',
'test/jio.storage/erp5storage.tests.js',
'test/jio.storage/fbstorage.tests.js',
'test/jio.storage/gdrivestorage.tests.js',
'test/jio.storage/memorystorage.tests.js',
'test/jio.storage/querystorage.tests.js',
'test/jio.storage/replicatestorage.tests.js',
'test/jio.storage/replicatestorage_fastrepair.tests.js',
'test/jio.storage/replicatestorage_fastrepairattachment.tests.js',
'test/jio.storage/replicatestorage_repair.tests.js',
'test/jio.storage/replicatestorage_repairattachment.tests.js',
'test/jio.storage/shastorage.tests.js',
'test/jio.storage/unionstorage.tests.js',
'test/jio.storage/uuidstorage.tests.js'
]
}, function (err) {
if (err) {
console.error('error', err);
}
if (report_data) {
console.log(JSON.stringify(report_data));
}
});
}(require, process, console, JSON));
/*
* Copyright 2018, Nexedi SA
*
* This program is free software: you can Use, Study, Modify and Redistribute
* it under the terms of the GNU General Public License version 3, or (at your
* option) any later version, as published by the Free Software Foundation.
*
* You can also Link and Combine this program with other software covered by
* the terms of any of the Free Software licenses or any of the Open Source
* Initiative approved licenses and Convey the resulting work. Corresponding
* source of such a combination shall include the source code for all other
* software used.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See COPYING file for full licensing terms.
* See https://www.nexedi.com/licensing for rationale and options.
*/
/*global require, global, Object */
(function (require, global, Object) {
"use strict";
var sinon = require('./sinon-require'),
jIO;
global.sinon = sinon;
global.XMLHttpRequest = sinon.FakeXMLHttpRequest;
jIO = require('../../dist/jio-latest-node');
global.jIO = jIO;
Object.keys(jIO.node_env).forEach(function (key) {
global[key] = jIO.node_env[key];
});
}(require, global, Object));
var sinon = require('sinon');
global.sinon = sinon;
// sinon/lib/sinon/util/event.js
(function () {
var push = [].push;
sinon.Event = function Event(type, bubbles, cancelable, target) {
this.initEvent(type, bubbles, cancelable, target);
};
sinon.Event.prototype = {
initEvent: function (type, bubbles, cancelable, target) {
this.type = type;
this.bubbles = bubbles;
this.cancelable = cancelable;
this.target = target;
},
stopPropagation: function () { },
preventDefault: function () {
this.defaultPrevented = true;
}
};
sinon.EventTarget = {
addEventListener: function addEventListener(event, listener, useCapture) {
this.eventListeners = this.eventListeners || {};
this.eventListeners[event] = this.eventListeners[event] || [];
push.call(this.eventListeners[event], listener);
},
removeEventListener: function removeEventListener(event, listener, useCapture) {
var listeners = this.eventListeners && this.eventListeners[event] || [];
for (var i = 0, l = listeners.length; i < l; ++i) {
if (listeners[i] == listener) {
return listeners.splice(i, 1);
}
}
},
dispatchEvent: function dispatchEvent(event) {
var type = event.type;
var listeners = this.eventListeners && this.eventListeners[type] || [];
for (var i = 0; i < listeners.length; i++) {
if (typeof listeners[i] == "function") {
listeners[i].call(this, event);
} else {
listeners[i].handleEvent(event);
}
}
return !!event.defaultPrevented;
}
};
}());
// sinon/lib/sinon/util/fake_server.js
sinon.fakeServer = (function () {
var push = [].push;
function F() { }
function create(proto) {
F.prototype = proto;
return new F();
}
function responseArray(handler) {
var response = handler;
if (Object.prototype.toString.call(handler) != "[object Array]") {
response = [200, {}, handler];
}
if (typeof response[2] != "string") {
throw new TypeError("Fake server response body should be string, but was " +
typeof response[2]);
}
return response;
}
var wloc = typeof window !== "undefined" ? window.location : {};
var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
function matchOne(response, reqMethod, reqUrl) {
var rmeth = response.method;
var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
var url = response.url;
var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
return matchMethod && matchUrl;
}
function match(response, request) {
var requestMethod = this.getHTTPMethod(request);
var requestUrl = request.url;
if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
requestUrl = requestUrl.replace(rCurrLoc, "");
}
if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
if (typeof response.response == "function") {
var ru = response.url;
var args = [request].concat(!ru ? [] : requestUrl.match(ru).slice(1));
return response.response.apply(response, args);
}
return true;
}
return false;
}
function log(response, request) {
var str;
str = "Request:\n" + sinon.format(request) + "\n\n";
str += "Response:\n" + sinon.format(response) + "\n\n";
sinon.log(str);
}
return {
create: function () {
var server = create(this);
this.xhr = sinon.useFakeXMLHttpRequest();
server.requests = [];
this.xhr.onCreate = function (xhrObj) {
server.addRequest(xhrObj);
};
return server;
},
addRequest: function addRequest(xhrObj) {
var server = this;
push.call(this.requests, xhrObj);
xhrObj.onSend = function () {
server.handleRequest(this);
};
if (this.autoRespond && !this.responding) {
setTimeout(function () {
server.responding = false;
server.respond();
}, this.autoRespondAfter || 10);
this.responding = true;
}
},
getHTTPMethod: function getHTTPMethod(request) {
if (this.fakeHTTPMethods && /post/i.test(request.method)) {
var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
return !!matches ? matches[1] : request.method;
}
return request.method;
},
handleRequest: function handleRequest(xhr) {
if (xhr.async) {
if (!this.queue) {
this.queue = [];
}
push.call(this.queue, xhr);
} else {
this.processRequest(xhr);
}
},
respondWith: function respondWith(method, url, body) {
if (arguments.length == 1 && typeof method != "function") {
this.response = responseArray(method);
return;
}
if (!this.responses) { this.responses = []; }
if (arguments.length == 1) {
body = method;
url = method = null;
}
if (arguments.length == 2) {
body = url;
url = method;
method = null;
}
push.call(this.responses, {
method: method,
url: url,
response: typeof body == "function" ? body : responseArray(body)
});
},
respond: function respond() {
if (arguments.length > 0) this.respondWith.apply(this, arguments);
var queue = this.queue || [];
var request;
while (request = queue.shift()) {
this.processRequest(request);
}
},
processRequest: function processRequest(request) {
try {
if (request.aborted) {
return;
}
var response = this.response || [404, {}, ""];
if (this.responses) {
for (var i = 0, l = this.responses.length; i < l; i++) {
if (match.call(this, this.responses[i], request)) {
response = this.responses[i].response;
break;
}
}
}
if (request.readyState != 4) {
log(response, request);
request.respond(response[0], response[1], response[2]);
}
} catch (e) {
sinon.logError("Fake server request processing", e);
}
},
restore: function restore() {
return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
}
};
}());
// sinon/lib/sinon/util/fake_xml_http_request.js
sinon.xhr = { XMLHttpRequest: global.XMLHttpRequest };
// wrapper for global
(function () {
var xhr = sinon.xhr;
xhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
xhr.GlobalActiveXObject = global.ActiveXObject;
xhr.supportsActiveX = typeof xhr.GlobalActiveXObject != "undefined";
xhr.supportsXHR = typeof xhr.GlobalXMLHttpRequest != "undefined";
xhr.workingXHR = xhr.supportsXHR ? xhr.GlobalXMLHttpRequest : xhr.supportsActiveX
? function () { return new xhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false;
/*jsl:ignore*/
var unsafeHeaders = {
"Accept-Charset": true,
"Accept-Encoding": true,
"Connection": true,
"Content-Length": true,
"Cookie": true,
"Cookie2": true,
"Content-Transfer-Encoding": true,
"Date": true,
"Expect": true,
"Host": true,
"Keep-Alive": true,
"Referer": true,
"TE": true,
"Trailer": true,
"Transfer-Encoding": true,
"Upgrade": true,
"User-Agent": true,
"Via": true
};
/*jsl:end*/
function FakeXMLHttpRequest() {
this.readyState = FakeXMLHttpRequest.UNSENT;
this.requestHeaders = {};
this.requestBody = null;
this.status = 0;
this.statusText = "";
var xhr = this;
var events = ["loadstart", "load", "abort", "loadend"];
function addEventListener(eventName) {
xhr.addEventListener(eventName, function (event) {
var listener = xhr["on" + eventName];
if (listener && typeof listener == "function") {
listener(event);
}
});
}
for (var i = events.length - 1; i >= 0; i--) {
addEventListener(events[i]);
}
if (typeof FakeXMLHttpRequest.onCreate == "function") {
FakeXMLHttpRequest.onCreate(this);
}
}
function verifyState(xhr) {
if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
throw new Error("INVALID_STATE_ERR");
}
if (xhr.sendFlag) {
throw new Error("INVALID_STATE_ERR");
}
}
// filtering to enable a white-list version of Sinon FakeXhr,
// where whitelisted requests are passed through to real XHR
function each(collection, callback) {
if (!collection) return;
for (var i = 0, l = collection.length; i < l; i += 1) {
callback(collection[i]);
}
}
function some(collection, callback) {
for (var index = 0; index < collection.length; index++) {
if (callback(collection[index]) === true) return true;
};
return false;
}
// largest arity in XHR is 5 - XHR#open
var apply = function (obj, method, args) {
switch (args.length) {
case 0: return obj[method]();
case 1: return obj[method](args[0]);
case 2: return obj[method](args[0], args[1]);
case 3: return obj[method](args[0], args[1], args[2]);
case 4: return obj[method](args[0], args[1], args[2], args[3]);
case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]);
};
};
FakeXMLHttpRequest.filters = [];
FakeXMLHttpRequest.addFilter = function (fn) {
this.filters.push(fn)
};
var IE6Re = /MSIE 6/;
FakeXMLHttpRequest.defake = function (fakeXhr, xhrArgs) {
var xhr = new sinon.xhr.workingXHR();
each(["open", "setRequestHeader", "send", "abort", "getResponseHeader",
"getAllResponseHeaders", "addEventListener", "overrideMimeType", "removeEventListener"],
function (method) {
fakeXhr[method] = function () {
return apply(xhr, method, arguments);
};
});
var copyAttrs = function (args) {
each(args, function (attr) {
try {
fakeXhr[attr] = xhr[attr]
} catch (e) {
if (!IE6Re.test(navigator.userAgent)) throw e;
}
});
};
var stateChange = function () {
fakeXhr.readyState = xhr.readyState;
if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
copyAttrs(["status", "statusText"]);
}
if (xhr.readyState >= FakeXMLHttpRequest.LOADING) {
copyAttrs(["responseText"]);
}
if (xhr.readyState === FakeXMLHttpRequest.DONE) {
copyAttrs(["responseXML"]);
}
if (fakeXhr.onreadystatechange) fakeXhr.onreadystatechange.call(fakeXhr);
};
if (xhr.addEventListener) {
for (var event in fakeXhr.eventListeners) {
if (fakeXhr.eventListeners.hasOwnProperty(event)) {
each(fakeXhr.eventListeners[event], function (handler) {
xhr.addEventListener(event, handler);
});
}
}
xhr.addEventListener("readystatechange", stateChange);
} else {
xhr.onreadystatechange = stateChange;
}
apply(xhr, "open", xhrArgs);
};
FakeXMLHttpRequest.useFilters = false;
function verifyRequestSent(xhr) {
if (xhr.readyState == FakeXMLHttpRequest.DONE) {
throw new Error("Request done");
}
}
function verifyHeadersReceived(xhr) {
if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
throw new Error("No headers received");
}
}
function verifyResponseBodyType(body) {
if (typeof body != "string") {
var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
body + ", which is not a string.");
error.name = "InvalidBodyException";
throw error;
}
}
sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {
async: true,
open: function open(method, url, async, username, password) {
this.method = method;
this.url = url;
this.async = typeof async == "boolean" ? async : true;
this.username = username;
this.password = password;
this.responseText = null;
this.responseXML = null;
this.requestHeaders = {};
this.sendFlag = false;
if (sinon.FakeXMLHttpRequest.useFilters === true) {
var xhrArgs = arguments;
var defake = some(FakeXMLHttpRequest.filters, function (filter) {
return filter.apply(this, xhrArgs)
});
if (defake) {
return sinon.FakeXMLHttpRequest.defake(this, arguments);
}
}
this.readyStateChange(FakeXMLHttpRequest.OPENED);
},
readyStateChange: function readyStateChange(state) {
this.readyState = state;
if (typeof this.onreadystatechange == "function") {
try {
this.onreadystatechange();
} catch (e) {
sinon.logError("Fake XHR onreadystatechange handler", e);
}
}
this.dispatchEvent(new sinon.Event("readystatechange"));
switch (this.readyState) {
case FakeXMLHttpRequest.DONE:
this.dispatchEvent(new sinon.Event("load", false, false, this));
this.dispatchEvent(new sinon.Event("loadend", false, false, this));
break;
}
},
setRequestHeader: function setRequestHeader(header, value) {
verifyState(this);
if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
throw new Error("Refused to set unsafe header \"" + header + "\"");
}
if (this.requestHeaders[header]) {
this.requestHeaders[header] += "," + value;
} else {
this.requestHeaders[header] = value;
}
},
// Helps testing
setResponseHeaders: function setResponseHeaders(headers) {
this.responseHeaders = {};
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
this.responseHeaders[header] = headers[header];
}
}
if (this.async) {
this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
} else {
this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
}
},
// Currently treats ALL data as a DOMString (i.e. no Document)
send: function send(data) {
verifyState(this);
if (!/^(get|head)$/i.test(this.method)) {
if (this.requestHeaders["Content-Type"]) {
var value = this.requestHeaders["Content-Type"].split(";");
this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8";
} else {
this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
}
this.requestBody = data;
}
this.errorFlag = false;
this.sendFlag = this.async;
this.readyStateChange(FakeXMLHttpRequest.OPENED);
if (typeof this.onSend == "function") {
this.onSend(this);
}
this.dispatchEvent(new sinon.Event("loadstart", false, false, this));
},
abort: function abort() {
this.aborted = true;
this.responseText = null;
this.errorFlag = true;
this.requestHeaders = {};
if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) {
this.readyStateChange(sinon.FakeXMLHttpRequest.DONE);
this.sendFlag = false;
}
this.readyState = sinon.FakeXMLHttpRequest.UNSENT;
this.dispatchEvent(new sinon.Event("abort", false, false, this));
if (typeof this.onerror === "function") {
this.onerror();
}
},
getResponseHeader: function getResponseHeader(header) {
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
return null;
}
if (/^Set-Cookie2?$/i.test(header)) {
return null;
}
header = header.toLowerCase();
for (var h in this.responseHeaders) {
if (h.toLowerCase() == header) {
return this.responseHeaders[h];
}
}
return null;
},
getAllResponseHeaders: function getAllResponseHeaders() {
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
return "";
}
var headers = "";
for (var header in this.responseHeaders) {
if (this.responseHeaders.hasOwnProperty(header) &&
!/^Set-Cookie2?$/i.test(header)) {
headers += header + ": " + this.responseHeaders[header] + "\r\n";
}
}
return headers;
},
setResponseBody: function setResponseBody(body) {
verifyRequestSent(this);
verifyHeadersReceived(this);
verifyResponseBodyType(body);
var chunkSize = this.chunkSize || 10;
var index = 0;
this.responseText = "";
do {
if (this.async) {
this.readyStateChange(FakeXMLHttpRequest.LOADING);
}
this.responseText += body.substring(index, index + chunkSize);
index += chunkSize;
} while (index < body.length);
var type = this.getResponseHeader("Content-Type");
if (this.responseText &&
(!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
try {
this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
} catch (e) {
// Unable to parse XML - no biggie
}
}
if (this.async) {
this.readyStateChange(FakeXMLHttpRequest.DONE);
} else {
this.readyState = FakeXMLHttpRequest.DONE;
}
},
respond: function respond(status, headers, body) {
this.setResponseHeaders(headers || {});
this.status = typeof status == "number" ? status : 200;
this.statusText = FakeXMLHttpRequest.statusCodes[this.status];
this.setResponseBody(body || "");
if (typeof this.onload === "function") {
this.onload();
}
}
});
sinon.extend(FakeXMLHttpRequest, {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4
});
// Borrowed from JSpec
FakeXMLHttpRequest.parseXML = function parseXML(text) {
var xmlDoc;
if (typeof DOMParser != "undefined") {
var parser = new DOMParser();
xmlDoc = parser.parseFromString(text, "text/xml");
} else {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(text);
}
return xmlDoc;
};
FakeXMLHttpRequest.statusCodes = {
100: "Continue",
101: "Switching Protocols",
200: "OK",
201: "Created",
202: "Accepted",
203: "Non-Authoritative Information",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
300: "Multiple Choice",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
307: "Temporary Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Request Entity Too Large",
414: "Request-URI Too Long",
415: "Unsupported Media Type",
416: "Requested Range Not Satisfiable",
417: "Expectation Failed",
422: "Unprocessable Entity",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported"
};
sinon.useFakeXMLHttpRequest = function () {
sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
if (xhr.supportsXHR) {
global.XMLHttpRequest = xhr.GlobalXMLHttpRequest;
}
if (xhr.supportsActiveX) {
global.ActiveXObject = xhr.GlobalActiveXObject;
}
delete sinon.FakeXMLHttpRequest.restore;
if (keepOnCreate !== true) {
delete sinon.FakeXMLHttpRequest.onCreate;
}
};
if (xhr.supportsXHR) {
global.XMLHttpRequest = sinon.FakeXMLHttpRequest;
}
if (xhr.supportsActiveX) {
global.ActiveXObject = function ActiveXObject(objId) {
if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
return new sinon.FakeXMLHttpRequest();
}
return new xhr.GlobalActiveXObject(objId);
};
}
return sinon.FakeXMLHttpRequest;
};
sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
})();
module.exports = sinon;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment