2014-04-13 23:34:05 +00:00
|
|
|
var stream = require("stream");
|
2014-12-14 03:43:05 +00:00
|
|
|
var util = require("util");
|
2016-10-11 20:28:36 +00:00
|
|
|
var Promise = require("promise");
|
2016-10-20 10:40:32 +00:00
|
|
|
var es = require("event-stream");
|
|
|
|
var combine = require("stream-combiner");
|
2014-04-13 23:34:05 +00:00
|
|
|
|
2014-04-07 00:33:49 +00:00
|
|
|
function isInBbox(position, bbox) {
|
|
|
|
if(position.lat > bbox.top || position.lat < bbox.bottom)
|
|
|
|
return false;
|
|
|
|
if(bbox.right < bbox.left) // bbox spans over lon = 180
|
|
|
|
return (position.lon > bbox.left || position.lon < bbox.right);
|
|
|
|
else
|
|
|
|
return (position.lon > bbox.left && position.lon < bbox.right);
|
|
|
|
}
|
|
|
|
|
2016-10-11 20:28:36 +00:00
|
|
|
function filterStreamPromise(inStream, filterFunction) {
|
2016-10-30 14:34:02 +00:00
|
|
|
var error = false;
|
|
|
|
|
|
|
|
var ret = new stream.Readable({ objectMode: true });
|
|
|
|
|
|
|
|
var running = false;
|
|
|
|
var queue = [ ];
|
|
|
|
|
|
|
|
function handleQueue() {
|
|
|
|
if(error || running)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(queue.length > 0) {
|
|
|
|
var next = queue.shift();
|
|
|
|
if(next == null) {
|
|
|
|
ret.push(null);
|
|
|
|
} else {
|
|
|
|
running = true;
|
|
|
|
Promise.resolve(filterFunction(next)).nodeify(function(err, newData) {
|
|
|
|
running = false;
|
|
|
|
|
|
|
|
if(error)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(err) {
|
|
|
|
error = true;
|
|
|
|
ret.emit("error", err);
|
|
|
|
} else if(newData != null) {
|
|
|
|
ret.push(newData);
|
|
|
|
}
|
|
|
|
|
|
|
|
setImmediate(handleQueue);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inStream.on("data", function(data) {
|
|
|
|
if(data != null)
|
|
|
|
queue.push(data);
|
|
|
|
handleQueue();
|
|
|
|
}).on("end", function() {
|
|
|
|
queue.push(null);
|
|
|
|
handleQueue();
|
|
|
|
}).on("error", function(err) {
|
|
|
|
ret.emit("error", err);
|
|
|
|
});
|
|
|
|
|
|
|
|
ret._read = function() {
|
|
|
|
};
|
|
|
|
|
|
|
|
return ret;
|
2014-04-26 16:23:22 +00:00
|
|
|
}
|
|
|
|
|
2014-04-14 02:43:40 +00:00
|
|
|
function extend(obj1, obj2) {
|
2014-05-10 16:57:08 +00:00
|
|
|
if(obj1 == null)
|
|
|
|
return null;
|
|
|
|
|
2014-04-14 02:43:40 +00:00
|
|
|
for(var i=1; i<arguments.length; i++) {
|
2014-05-10 16:57:08 +00:00
|
|
|
if(arguments[i] == null)
|
|
|
|
continue;
|
|
|
|
|
2014-04-14 02:43:40 +00:00
|
|
|
for(var j in arguments[i]) {
|
|
|
|
obj1[j] = arguments[i][j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return obj1;
|
|
|
|
}
|
|
|
|
|
|
|
|
function calculateDistance(posList) {
|
|
|
|
// From http://stackoverflow.com/a/365853/242365
|
|
|
|
|
|
|
|
var R = 6371; // km
|
|
|
|
var ret = 0;
|
|
|
|
|
|
|
|
for(var i=1; i<posList.length; i++) {
|
|
|
|
var lat1 = posList[i-1].lat * Math.PI / 180;
|
|
|
|
var lon1 = posList[i-1].lon * Math.PI / 180;
|
|
|
|
var lat2 = posList[i].lat * Math.PI / 180;
|
|
|
|
var lon2 = posList[i].lon * Math.PI / 180;
|
|
|
|
var dLat = lat2-lat1;
|
|
|
|
var dLon = lon2-lon1;
|
|
|
|
|
|
|
|
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
|
|
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
|
|
|
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
|
|
ret += R * c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-05-10 16:51:51 +00:00
|
|
|
var LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
|
|
|
|
|
|
function generateRandomId(length) {
|
|
|
|
var randomPadId = "";
|
|
|
|
for(var i=0; i<length; i++) {
|
|
|
|
randomPadId += LETTERS[Math.floor(Math.random() * LETTERS.length)];
|
|
|
|
}
|
|
|
|
return randomPadId;
|
|
|
|
}
|
|
|
|
|
2014-12-11 01:42:32 +00:00
|
|
|
var FAILURE = { };
|
|
|
|
|
|
|
|
function stripObject(obj, structure) {
|
|
|
|
return _stripObject(obj, structure) !== FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _stripObject(obj, type) {
|
|
|
|
if(obj === undefined)
|
|
|
|
return obj;
|
|
|
|
else if(obj === null)
|
|
|
|
return obj;
|
|
|
|
else if(type instanceof Array) {
|
|
|
|
if(!(obj instanceof Array))
|
|
|
|
return FAILURE;
|
|
|
|
|
|
|
|
for(var i=0; i<obj.length; i++) {
|
|
|
|
if((obj[i] = _stripObject(obj[i], type[0])) === FAILURE)
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
else if(typeof type == "function")
|
|
|
|
return (obj instanceof type) ? obj : FAILURE;
|
|
|
|
else if(type instanceof Object) {
|
|
|
|
if(!(obj instanceof Object))
|
|
|
|
return FAILURE;
|
|
|
|
|
|
|
|
for(var i in obj) {
|
|
|
|
if(type[i] == null || obj[i] === undefined)
|
|
|
|
delete obj[i];
|
|
|
|
else if((obj[i] = _stripObject(obj[i], type[i])) === FAILURE)
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
else if(type == "number" && typeof obj == "string")
|
2016-10-24 17:39:59 +00:00
|
|
|
return obj == "" ? null : isNaN(obj = 1*obj) ? FAILURE : obj;
|
2014-12-11 01:42:32 +00:00
|
|
|
else if(type == "string" && typeof obj == "number")
|
|
|
|
return ""+obj;
|
|
|
|
else if(typeof type == "string")
|
|
|
|
return (typeof obj == type) ? obj : FAILURE;
|
|
|
|
else
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-12-14 03:43:05 +00:00
|
|
|
function ArrayStream(array) {
|
|
|
|
stream.Readable.call(this, { objectMode: true });
|
|
|
|
|
|
|
|
if(array != null)
|
2016-02-28 16:53:13 +00:00
|
|
|
setImmediate(function(){ this.receiveArray(null, array); }.bind(this));
|
2014-12-14 03:43:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
util.inherits(ArrayStream, stream.Readable);
|
|
|
|
|
|
|
|
ArrayStream.prototype._read = function(size) {
|
|
|
|
};
|
|
|
|
|
|
|
|
ArrayStream.prototype.receiveArray = function(err, array) {
|
|
|
|
if(err)
|
|
|
|
return this.emit("error", err);
|
|
|
|
|
|
|
|
for(var i=0; i<array.length; i++)
|
|
|
|
this.push(array[i]);
|
2016-02-28 16:53:13 +00:00
|
|
|
this.push(null);
|
2014-12-14 03:43:05 +00:00
|
|
|
};
|
|
|
|
|
2016-10-11 20:28:36 +00:00
|
|
|
function streamEachPromise(stream, handle) {
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
var ended = false;
|
|
|
|
var error = false;
|
|
|
|
var reading = false;
|
|
|
|
|
|
|
|
var read = function() {
|
|
|
|
if(error)
|
|
|
|
return reject(error);
|
|
|
|
|
|
|
|
reading = true;
|
2014-12-14 03:43:05 +00:00
|
|
|
|
|
|
|
var item = stream.read();
|
|
|
|
if(item != null)
|
2016-10-11 20:28:36 +00:00
|
|
|
Promise.resolve().then(function() { return handle(item) }).then(read, reject);
|
|
|
|
else if(!ended) {
|
|
|
|
reading = false;
|
|
|
|
stream.once("readable", read);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
resolve();
|
|
|
|
};
|
2014-12-14 03:43:05 +00:00
|
|
|
|
2016-10-11 20:28:36 +00:00
|
|
|
stream.on("end", function() {
|
|
|
|
ended = true;
|
2014-12-14 03:43:05 +00:00
|
|
|
|
2016-10-11 20:28:36 +00:00
|
|
|
if(!reading)
|
|
|
|
read();
|
|
|
|
});
|
|
|
|
|
|
|
|
stream.on("error", function(err) {
|
|
|
|
error = err;
|
|
|
|
|
|
|
|
if(!reading)
|
|
|
|
read();
|
|
|
|
});
|
2014-12-14 03:43:05 +00:00
|
|
|
|
2016-10-11 20:28:36 +00:00
|
|
|
read();
|
2014-12-14 03:43:05 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-09-28 12:10:08 +00:00
|
|
|
function escapeXml(str) {
|
|
|
|
return ("" + str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
|
|
}
|
|
|
|
|
|
|
|
function isoDate(date) {
|
|
|
|
if(!date)
|
|
|
|
date = new Date();
|
|
|
|
|
|
|
|
function pad(number, length) {
|
|
|
|
number = "" + number;
|
|
|
|
while(number.length < length)
|
|
|
|
number = "0" + number;
|
|
|
|
return number;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pad(date.getUTCFullYear(), 4) + '-' + pad(date.getUTCMonth()+1, 2) + '-' + pad(date.getUTCDate(), 2) + 'T' + pad(date.getUTCHours(), 2) + ':' + pad(date.getUTCMinutes(), 2) + ':' + pad(date.getUTCSeconds(), 2) + 'Z';
|
|
|
|
}
|
|
|
|
|
2016-10-19 08:56:06 +00:00
|
|
|
function round(number, digits) {
|
|
|
|
var fac = Math.pow(10, digits);
|
|
|
|
return Math.round(number*fac)/fac;
|
|
|
|
}
|
|
|
|
|
2016-10-20 10:40:32 +00:00
|
|
|
function streamToArrayPromise(stream) {
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
var writer = es.writeArray(function(err, array) {
|
|
|
|
if(err)
|
|
|
|
reject(err);
|
|
|
|
else
|
|
|
|
resolve(array);
|
|
|
|
});
|
|
|
|
|
|
|
|
stream.pipe(writer);
|
|
|
|
stream.on("error", reject);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function promiseAllObject(obj) {
|
|
|
|
var keys = [ ];
|
|
|
|
var values = [ ];
|
|
|
|
for(var i in obj) {
|
|
|
|
keys.push(i);
|
|
|
|
values.push(Promise.resolve(obj[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.all(values).then(function(objs) {
|
|
|
|
var ret = { };
|
|
|
|
for(var i=0; i<objs.length; i++)
|
|
|
|
ret[keys[i]] = objs[i];
|
|
|
|
return ret;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-30 14:01:21 +00:00
|
|
|
function promiseAuto(obj) {
|
|
|
|
var promises = { };
|
|
|
|
|
|
|
|
function _get(str) {
|
|
|
|
if(!obj[str])
|
|
|
|
throw new Error("Invalid dependency '" + str + "' in promiseAuto().");
|
|
|
|
|
|
|
|
if(promises[str])
|
|
|
|
return promises[str];
|
|
|
|
|
|
|
|
if(obj[str].then)
|
|
|
|
return obj[str];
|
|
|
|
|
|
|
|
var params = getFuncParams(obj[str]);
|
|
|
|
return promises[str] = _getDeps(params).then(function(res) {
|
|
|
|
return obj[str].apply(null, params.map(function(param) { return res[param]; }));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function _getDeps(arr) {
|
|
|
|
var deps = { };
|
|
|
|
arr.forEach(function(it) {
|
|
|
|
deps[it] = _get(it);
|
|
|
|
});
|
|
|
|
return promiseAllObject(deps);
|
|
|
|
}
|
|
|
|
|
|
|
|
return _getDeps(Object.keys(obj));
|
|
|
|
}
|
|
|
|
|
|
|
|
function getFuncParams(func) {
|
|
|
|
// Taken from angular injector code
|
|
|
|
|
|
|
|
var ARROW_ARG = /^([^\(]+?)\s*=>/;
|
|
|
|
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
|
|
|
|
var FN_ARG_SPLIT = /\s*,\s*/;
|
|
|
|
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
|
|
|
|
|
|
|
var fnText = (Function.prototype.toString.call(func) + ' ').replace(STRIP_COMMENTS, '');
|
|
|
|
var params = (fnText.match(ARROW_ARG) || fnText.match(FN_ARGS))[1];
|
|
|
|
return params == "" ? [ ] : params.split(FN_ARG_SPLIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
function modifyFunction(obj, prop, before, after) {
|
|
|
|
var bkp = obj[prop];
|
|
|
|
obj[prop] = function() {
|
|
|
|
before && before.apply(this, arguments);
|
|
|
|
var ret = bkp.apply(this, arguments);
|
|
|
|
after && after.apply(this, arguments);
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-02-10 11:12:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Intercepts the "write" and "end" methods of the given writable stream and buffers the values written to them instead.
|
|
|
|
* When the stream ends, the buffered value is returned and the original "write" and "end" methods are restored.
|
|
|
|
* @param writeStream {stream.Writable}
|
|
|
|
* @returns {Promise.<string>}
|
|
|
|
*/
|
|
|
|
function interceptWriteStream(writeStream) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let response = "";
|
|
|
|
let writeBkp = writeStream.write;
|
|
|
|
let sendBkp = writeStream.send; // For express response streams
|
|
|
|
let endBkp = writeStream.end;
|
|
|
|
|
|
|
|
writeStream.write = function(chunk, encoding, callback) {
|
|
|
|
response += chunk;
|
|
|
|
|
|
|
|
if(typeof encoding == "function") {
|
|
|
|
encoding();
|
|
|
|
} else if(callback) {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
writeStream.send = function(body) {
|
|
|
|
writeStream.end(body);
|
|
|
|
};
|
|
|
|
|
|
|
|
writeStream.end = function(chunk, encoding, callback) {
|
|
|
|
response += chunk;
|
|
|
|
|
|
|
|
writeStream.write = writeBkp;
|
|
|
|
writeStream.send = sendBkp;
|
|
|
|
writeStream.end = endBkp;
|
|
|
|
|
|
|
|
if(typeof encoding == "function") {
|
|
|
|
writeStream.once("finish", encoding);
|
|
|
|
} else if(callback) {
|
|
|
|
writeStream.once("finish", encoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(response);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-04-07 00:33:49 +00:00
|
|
|
module.exports = {
|
2014-04-13 23:34:05 +00:00
|
|
|
isInBbox : isInBbox,
|
2016-10-11 20:28:36 +00:00
|
|
|
filterStreamPromise : filterStreamPromise,
|
2014-04-14 02:43:40 +00:00
|
|
|
extend : extend,
|
2014-04-28 16:35:27 +00:00
|
|
|
calculateDistance : calculateDistance,
|
2014-12-11 01:42:32 +00:00
|
|
|
generateRandomId : generateRandomId,
|
2014-12-14 03:43:05 +00:00
|
|
|
stripObject : stripObject,
|
|
|
|
ArrayStream : ArrayStream,
|
2016-10-11 20:28:36 +00:00
|
|
|
streamEachPromise : streamEachPromise,
|
2016-09-28 12:10:08 +00:00
|
|
|
escapeXml : escapeXml,
|
2016-10-19 08:56:06 +00:00
|
|
|
isoDate : isoDate,
|
2016-10-20 10:40:32 +00:00
|
|
|
round: round,
|
|
|
|
streamToArrayPromise: streamToArrayPromise,
|
2016-10-30 14:01:21 +00:00
|
|
|
promiseAllObject: promiseAllObject,
|
|
|
|
promiseAuto: promiseAuto,
|
2017-02-10 11:12:47 +00:00
|
|
|
modifyFunction: modifyFunction,
|
|
|
|
interceptWriteStream: interceptWriteStream
|
2014-04-07 00:33:49 +00:00
|
|
|
};
|