Commit a1e93301 authored by Sven Franck's avatar Sven Franck

libs: update JIO and localStorage to latest to fix bugs

parent ac0a7350
......@@ -549,7 +549,7 @@ function inherits(constructor, superConstructor) {
}
/**
* Clones jsonable object in deep
* Clones jsonable object in depth
*
* @param {A} object The jsonable object to clone
* @return {A} The cloned object
......@@ -570,7 +570,7 @@ exports.util.jsonDeepClone = jsonDeepClone;
* It can also clone object which are serializable, like Date.
*
* To make a class serializable, you need to implement the `toJSON` function
* which returns a JSON representation of the object. The return value is used
* which returns a JSON representation of the object. The returned value is used
* as first parameter of the object constructor.
*
* @param {A} object The object to clone
......@@ -600,12 +600,12 @@ function deepClone(object) {
}
if (object instanceof Date) {
// XXX this block is to enable phantomjs and browsers compatibility with
// Date.prototype.toJSON when it is a invalid date. In phantomjs, it
// Date.prototype.toJSON when it is an invalid date. In phantomjs, it
// returns `"Invalid Date"` but in browsers it returns `null`. In
// browsers, give `null` as parameter to `new Date()` doesn't return an
// browsers, giving `null` as parameter to `new Date()` doesn't return an
// invalid date.
// Clonning date with `return new Date(object)` make problems on Firefox.
// Cloning a date with `return new Date(object)` has problems on Firefox.
// I don't know why... (Tested on Firefox 23)
if (isFinite(object.getTime())) {
......@@ -625,7 +625,7 @@ function deepClone(object) {
exports.util.deepClone = deepClone;
/**
* Update a dictionnary by adding/replacing key values from another dict.
* Update a dictionary by adding/replacing key values from another dict.
* Enumerable values equal to undefined are also used.
*
* @param {Object} original The dict to update
......@@ -687,16 +687,14 @@ exports.util.dictFilter = dictFilter;
* @return {Object} The type dict
*/
function arrayValuesToTypeDict(array) {
var i, type, types = {};
for (i = 0; i < array.length; i += 1) {
type = Array.isArray(array[i]) ? 'array' : typeof array[i];
if (!types[type]) {
types[type] = [array[i]];
} else {
types[type][types[type].length] = array[i];
}
}
return types;
var i, l, type_object = {}, type, v;
for (i = 0, l = array.length; i < l; i += 1) {
v = array[i];
type = Array.isArray(v) ? "array" : typeof v;
/*jslint ass: true */
(type_object[type] = type_object[type] || []).push(v);
}
return type_object;
}
/**
......@@ -817,7 +815,7 @@ exports.util.readBlobAsText = readBlobAsText;
/**
* Send request with XHR and return a promise. xhr.onload: The promise is
* resolve when the status code is lower than 400 with the xhr object as first
* resolved when the status code is lower than 400 with the xhr object as first
* parameter. xhr.onerror: reject with xhr object as first
* parameter. xhr.onprogress: notifies the xhr object.
*
......@@ -826,7 +824,7 @@ exports.util.readBlobAsText = readBlobAsText;
* @param {String} [param.dataType=""] The data type to retrieve
* @param {String} param.url The url
* @param {Any} [param.data] The data to send
* @param {Function} [param.beforeSend] A function called just before send
* @param {Function} [param.beforeSend] A function called just before the send
* request. The first parameter of this function is the XHR object.
* @return {Promise} The promise
*/
......@@ -851,9 +849,6 @@ function ajax(param) {
});
xhr.addEventListener("error", reject);
xhr.addEventListener("progress", notify);
if (typeof param.beforeSend === 'function') {
param.beforeSend(xhr);
}
if (typeof param.xhrFields === 'object' && param.xhrFields !== null) {
for (k in param.xhrFields) {
if (param.xhrFields.hasOwnProperty(k)) {
......@@ -861,6 +856,9 @@ function ajax(param) {
}
}
}
if (typeof param.beforeSend === 'function') {
param.beforeSend(xhr);
}
xhr.send(param.data);
}, function () {
xhr.abort();
......@@ -933,6 +931,169 @@ function methodType(method) {
}
}
/**
* forEach(array, callback[, thisArg]): Promise
*
* It executes the provided `callback` once for each element of the array with
* an assigned value asynchronously. If the `callback` returns a promise, then
* the function will wait for its fulfillment before executing the next
* iteration.
*
* `callback` is invoked with three arguments:
*
* - the element value
* - the element index
* - the array being traversed
*
* If a `thisArg` parameter is provided to `forEach`, it will be passed to
* `callback` when invoked, for use as its `this` value. Otherwise, the value
* `undefined` will be passed for use as its `this` value.
*
* Unlike `Array.prototype.forEach`, you can stop the iteration by throwing
* something, or by doing a `cancel` to the returned promise if it is
* cancellable promise.
*
* Inspired by `Array.prototype.forEach` from Mozilla Developer Network.
*
* @param {Array} array The array to parse
* @param {Function} callback Function to execute for each element.
* @param {Any} [thisArg] Value to use as `this` when executing `callback`.
* @param {Promise} A new promise.
*/
function forEach(array, fn, thisArg) {
if (arguments.length === 0) {
throw new TypeError("missing argument 0 when calling function forEach");
}
if (!Array.isArray(array)) {
throw new TypeError(array + " is not an array");
}
if (arguments.length === 1) {
throw new TypeError("missing argument 1 when calling function forEach");
}
if (typeof fn !== "function") {
throw new TypeError(fn + " is not a function");
}
var cancelled, current_promise = RSVP.resolve();
return new RSVP.Promise(function (done, fail, notify) {
var i = 0;
function next() {
if (cancelled) {
fail(new Error("Cancelled"));
return;
}
if (i < array.length) {
current_promise =
current_promise.then(fn.bind(thisArg, array[i], i, array));
current_promise.then(next, fail, notify);
i += 1;
return;
}
done();
}
next();
}, function () {
cancelled = true;
if (typeof current_promise.cancel === "function") {
current_promise.cancel();
}
});
}
exports.util.forEach = forEach;
/**
* range(stop, callback): Promise
* range(start, stop[, step], callback): Promise
*
* It executes the provided `callback` once for each step between `start` and
* `stop`. If the `callback` returns a promise, then the function will wait
* for its fulfillment before executing the next iteration.
*
* `callback` is invoked with one argument:
*
* - the index of the step
*
* `start`, `stop` and `step` must be finite numbers. If `step` is not
* provided, then the default step will be `1`. If `start` and `step` are not
* provided, `start` will be `0` and `step` will be `1`.
*
* Inspired by `range()` from Python 3 built-in functions.
*
* range(10, function (index) {
* return notifyIndex(index);
* }).then(onDone, onError, onNotify);
*
* @param {Number} [start=0] The start index
* @param {Number} stop The stop index
* @param {Number} [step=1] One step
* @param {Function} callback Function to execute on each iteration.
* @param {Promise} A new promise with no fulfillment value.
*/
function range(start, stop, step, callback) {
var type_object, cancelled, current_promise;
type_object = arrayValuesToTypeDict([start, stop, step, callback]);
if (type_object["function"].length !== 1) {
throw new TypeError("range(): only one callback is needed");
}
start = type_object.number.length;
if (start < 1) {
throw new TypeError("range(): 1, 2 or 3 numbers are needed");
}
if (start > 3) {
throw new TypeError("range(): only 1, 2 or 3 numbers are needed");
}
callback = type_object["function"][0];
if (start === 1) {
start = 0;
stop = type_object.number[0];
step = 1;
}
if (start === 2) {
start = type_object.number[0];
stop = type_object.number[1];
step = 1;
}
if (start === 3) {
start = type_object.number[0];
stop = type_object.number[1];
step = type_object.number[2];
if (step === 0) {
throw new TypeError("range(): step must not be zero");
}
}
type_object = undefined;
current_promise = RSVP.resolve();
return new RSVP.Promise(function (done, fail, notify) {
var i = start, test;
function next() {
if (cancelled) {
fail(new Error("Cancelled"));
return;
}
test = step > 0 ? i < stop : i > stop;
if (test) {
current_promise = current_promise.then(callback.bind(null, i));
current_promise.then(next, fail, notify);
i += step;
return;
}
done();
}
next();
}, function () {
cancelled = true;
if (typeof current_promise.cancel === "function") {
current_promise.cancel();
}
});
}
exports.util.range = range;
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global secureMethods, exports, console */
......@@ -2119,7 +2280,7 @@ function addJobRuleCondition(name, method) {
exports.addJobRuleCondition = addJobRuleCondition;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global constants, dictUpdate, deepClone */
/*global constants, dictUpdate, deepClone, DOMException */
function restCommandRejecter(param, args) {
// reject(status, reason, message, {"custom": "value"});
......@@ -2192,8 +2353,22 @@ function restCommandRejecter(param, args) {
if ((arg.statusText || arg.status >= 0)) {
current_priority.status = arg.statusText || arg.status;
}
if (arg instanceof Error) {
current_priority.reason = arg.message || "";
if (arg instanceof Error || arg instanceof DOMException) {
if (arg.code !== undefined && arg.code !== null) {
current_priority.code = arg.code;
}
if (arg.lineNumber !== undefined && arg.lineNumber !== null) {
current_priority.lineNumber = arg.lineNumber;
}
if (arg.columnNumber !== undefined && arg.columnNumber !== null) {
current_priority.columnNumber = arg.columnNumber;
}
if (arg.filename !== undefined && arg.filename !== null) {
current_priority.filename = arg.filename;
}
if (arg.message !== undefined && arg.message !== null) {
current_priority.reason = arg.message;
}
current_priority.error = arg.name;
}
}
......@@ -2224,6 +2399,7 @@ function restCommandRejecter(param, args) {
priority.error = priority.statusText.toLowerCase().replace(/ /g, '_').
replace(/[^_a-z]/g, '');
}
param.storage_response = priority;
return param.solver.reject(deepClone(priority));
}
......@@ -2250,8 +2426,8 @@ function restCommandResolver(param, args) {
args = Array.prototype.slice.call(args);
arg = args.shift();
// priority 3 - never change
current_priority = priority[3];
// priority 4 - never change
current_priority = priority[4];
if (param.kwargs._id) {
current_priority.id = param.kwargs._id;
}
......@@ -2350,6 +2526,8 @@ function restCommandResolver(param, args) {
param.method + ' method needs a dict as returned "data".'
]);
}
param.storage_response = priority;
return param.solver.resolve(deepClone(priority));
}
......@@ -2372,8 +2550,6 @@ function enableJobChecker(jio, shared, options) {
// emits 'job:modified', 'job:start', 'job:resolved',
// 'job:end', 'job:reject' events
var i;
shared.job_rule_action_names = [undefined, "ok", "wait", "update", "deny"];
shared.job_rule_actions = {
......@@ -2398,11 +2574,11 @@ function enableJobChecker(jio, shared, options) {
if (!original_job.solver) {
original_job.solver = new_job.solver;
} else {
original_job.promise.then(
new_job.command.resolve,
new_job.command.reject,
new_job.command.notify
);
original_job.promise.then(function () {
new_job.command.resolve(deepClone(original_job.storage_response));
}, function () {
new_job.command.reject(deepClone(original_job.storage_response));
}, new_job.command.notify);
}
}
new_job.state = 'running';
......@@ -2555,6 +2731,8 @@ function enableJobChecker(jio, shared, options) {
}
}
var index;
if (options.job_management !== false) {
shared.job_rules = [{
......@@ -2594,8 +2772,8 @@ function enableJobChecker(jio, shared, options) {
}
if (Array.isArray(options.job_rules)) {
for (i = 0; i < options.job_rules.length; i += 1) {
addJobRule(deepClone(options.job_rules[i]));
for (index = 0; index < options.job_rules.length; index += 1) {
addJobRule(deepClone(options.job_rules[index]));
}
}
......@@ -2949,9 +3127,10 @@ function enableJobRecovery(jio, shared, options) {
var i, job_array, delay, deadline, recovery_delay;
recovery_delay = numberOrDefault(options.recovery_delay, 10000);
// 1 m 30 s === default firefox request timeout
recovery_delay = numberOrDefault(options.recovery_delay, 90000);
if (recovery_delay < 0) {
recovery_delay = 10000;
recovery_delay = 90000;
}
if (options.job_management !== false && options.job_recovery !== false) {
......@@ -3152,8 +3331,8 @@ function enableJobTimeout(jio, shared, options) {
number : default_value);
}
// 10 seconds by default
var default_timeout = positiveNumberOrDefault(options.default_timeout, 10000);
// Infinity by default
var default_timeout = positiveNumberOrDefault(options.default_timeout, 0);
function timeoutReject(param) {
return function () {
......@@ -3168,7 +3347,8 @@ function enableJobTimeout(jio, shared, options) {
}
function initJob(job) {
if (typeof job.timeout !== 'number' || job.timeout < 0) {
if (typeof job.timeout !== 'number' || !isFinite(job.timeout) ||
job.timeout < 0) {
job.timeout = positiveNumberOrDefault(
job.options.timeout,
default_timeout
......@@ -3258,7 +3438,7 @@ function enableRestAPI(jio, shared) { // (jio, shared, options)
}
param.kwargs = deepClone(param.kwargs);
} else {
param.kwargs = deepClone(type_dict.object.shift());
param.kwargs = {};
}
param.solver = {};
param.options = deepClone(type_dict.object.shift()) || {};
......@@ -3427,4 +3607,1886 @@ function enableRestParamChecker(jio, shared) {
}
/*jslint indent: 2, maxlen: 80, sloppy: true */
var query_class_dict = {};
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:
true, select: true, exports, stringEscapeRegexpCharacters: true,
deepClone, RSVP, sequence */
/**
* The query to use to filter a list of objects.
* This is an abstract class.
*
* @class Query
* @constructor
*/
function Query() {
/**
* Called before parsing the query. Must be overridden!
*
* @method onParseStart
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseStart = emptyFunction;
/**
* Called when parsing a simple query. Must be overridden!
*
* @method onParseSimpleQuery
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseSimpleQuery = emptyFunction;
/**
* Called when parsing a complex query. Must be overridden!
*
* @method onParseComplexQuery
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseComplexQuery = emptyFunction;
/**
* Called after parsing the query. Must be overridden!
*
* @method onParseEnd
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseEnd = emptyFunction;
}
/**
* Filter the item list with matching item only
*
* @method exec
* @param {Array} item_list The list of object
* @param {Object} [option] Some operation option
* @param {Array} [option.select_list] A object keys to retrieve
* @param {Array} [option.sort_on] Couples of object keys and "ascending"
* or "descending"
* @param {Array} [option.limit] Couple of integer, first is an index and
* second is the length.
*/
Query.prototype.exec = function (item_list, option) {
var i, promises = [];
if (!Array.isArray(item_list)) {
throw new TypeError("Query().exec(): Argument 1 is not of type 'array'");
}
if (option === undefined) {
option = {};
}
if (typeof option !== 'object') {
throw new TypeError("Query().exec(): " +
"Optional argument 2 is not of type 'object'");
}
for (i = 0; i < item_list.length; i += 1) {
if (!item_list[i]) {
promises.push(RSVP.resolve(false));
} else {
promises.push(this.match(item_list[i]));
}
}
return sequence([function () {
return RSVP.all(promises);
}, function (answers) {
var j;
for (j = answers.length - 1; j >= 0; j -= 1) {
if (!answers[j]) {
item_list.splice(j, 1);
}
}
if (option.sort_on) {
return sortOn(option.sort_on, item_list);
}
}, function () {
if (option.limit) {
return limit(option.limit, item_list);
}
}, function () {
return select(option.select_list || [], item_list);
}, function () {
return item_list;
}]);
};
/**
* Test if an item matches this query
*
* @method match
* @param {Object} item The object to test
* @return {Boolean} true if match, false otherwise
*/
Query.prototype.match = function () {
return RSVP.resolve(true);
};
/**
* Browse the Query in deep calling parser method in each step.
*
* `onParseStart` is called first, on end `onParseEnd` is called.
* It starts from the simple queries at the bottom of the tree calling the
* parser method `onParseSimpleQuery`, and go up calling the
* `onParseComplexQuery` method.
*
* @method parse
* @param {Object} option Any options you want (except 'parsed')
* @return {Any} The parse result
*/
Query.prototype.parse = function (option) {
var that = this, object;
/**
* The recursive parser.
*
* @param {Object} object The object shared in the parse process
* @param {Object} options Some options usable in the parseMethods
* @return {Any} The parser result
*/
function recParse(object, option) {
var query = object.parsed;
if (query.type === "complex") {
return sequence([function () {
return sequence(query.query_list.map(function (v, i) {
/*jslint unparam: true */
return function () {
return sequence([function () {
object.parsed = query.query_list[i];
return recParse(object, option);
}, function () {
query.query_list[i] = object.parsed;
}]);
};
}));
}, function () {
object.parsed = query;
return that.onParseComplexQuery(object, option);
}]);
}
if (query.type === "simple") {
return that.onParseSimpleQuery(object, option);
}
}
object = {"parsed": JSON.parse(JSON.stringify(that.serialized()))};
return sequence([function () {
return that.onParseStart(object, option);
}, function () {
return recParse(object, option);
}, function () {
return that.onParseEnd(object, option);
}, function () {
return object.parsed;
}]);
};
/**
* Convert this query to a parsable string.
*
* @method toString
* @return {String} The string version of this query
*/
Query.prototype.toString = function () {
return "";
};
/**
* Convert this query to an jsonable object in order to be remake thanks to
* QueryFactory class.
*
* @method serialized
* @return {Object} The jsonable object
*/
Query.prototype.serialized = function () {
return undefined;
};
exports.Query = Query;
/**
* Parse a text request to a json query object tree
*
* @param {String} string The string to parse
* @return {Object} The json query tree
*/
function parseStringToObject(string) {
/*
Default template driver for JS/CC generated parsers running as
browser-based JavaScript/ECMAScript applications.
WARNING: This parser template will not run as console and has lesser
features for debugging than the console derivates for the
various JavaScript platforms.
Features:
- Parser trace messages
- Integrated panic-mode error recovery
Written 2007, 2008 by Jan Max Meyer, J.M.K S.F. Software Technologies
This is in the public domain.
*/
var NODEJS__dbg_withtrace = false;
var NODEJS__dbg_string = new String();
function __NODEJS_dbg_print( text )
{
NODEJS__dbg_string += text + "\n";
}
function __NODEJS_lex( info )
{
var state = 0;
var match = -1;
var match_pos = 0;
var start = 0;
var pos = info.offset + 1;
do
{
pos--;
state = 0;
match = -2;
start = pos;
if( info.src.length <= start )
return 19;
do
{
switch( state )
{
case 0:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 8 ) || ( info.src.charCodeAt( pos ) >= 10 && info.src.charCodeAt( pos ) <= 31 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || info.src.charCodeAt( pos ) == 59 || ( info.src.charCodeAt( pos ) >= 63 && info.src.charCodeAt( pos ) <= 64 ) || ( info.src.charCodeAt( pos ) >= 66 && info.src.charCodeAt( pos ) <= 77 ) || ( info.src.charCodeAt( pos ) >= 80 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 9 ) state = 2;
else if( info.src.charCodeAt( pos ) == 40 ) state = 3;
else if( info.src.charCodeAt( pos ) == 41 ) state = 4;
else if( info.src.charCodeAt( pos ) == 60 || info.src.charCodeAt( pos ) == 62 ) state = 5;
else if( info.src.charCodeAt( pos ) == 33 ) state = 11;
else if( info.src.charCodeAt( pos ) == 79 ) state = 12;
else if( info.src.charCodeAt( pos ) == 32 ) state = 13;
else if( info.src.charCodeAt( pos ) == 61 ) state = 14;
else if( info.src.charCodeAt( pos ) == 34 ) state = 15;
else if( info.src.charCodeAt( pos ) == 65 ) state = 19;
else if( info.src.charCodeAt( pos ) == 78 ) state = 20;
else state = -1;
break;
case 1:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 10;
match_pos = pos;
break;
case 2:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 1;
match_pos = pos;
break;
case 3:
state = -1;
match = 3;
match_pos = pos;
break;
case 4:
state = -1;
match = 4;
match_pos = pos;
break;
case 5:
if( info.src.charCodeAt( pos ) == 61 ) state = 14;
else state = -1;
match = 11;
match_pos = pos;
break;
case 6:
state = -1;
match = 8;
match_pos = pos;
break;
case 7:
state = -1;
match = 9;
match_pos = pos;
break;
case 8:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 6;
match_pos = pos;
break;
case 9:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 5;
match_pos = pos;
break;
case 10:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 7;
match_pos = pos;
break;
case 11:
if( info.src.charCodeAt( pos ) == 61 ) state = 14;
else state = -1;
break;
case 12:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 81 ) || ( info.src.charCodeAt( pos ) >= 83 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 82 ) state = 8;
else state = -1;
match = 10;
match_pos = pos;
break;
case 13:
state = -1;
match = 1;
match_pos = pos;
break;
case 14:
state = -1;
match = 11;
match_pos = pos;
break;
case 15:
if( info.src.charCodeAt( pos ) == 34 ) state = 7;
else if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 15;
else if( info.src.charCodeAt( pos ) == 92 ) state = 17;
else state = -1;
break;
case 16:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 67 ) || ( info.src.charCodeAt( pos ) >= 69 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 68 ) state = 9;
else state = -1;
match = 10;
match_pos = pos;
break;
case 17:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 254 ) ) state = 15;
else state = -1;
break;
case 18:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 83 ) || ( info.src.charCodeAt( pos ) >= 85 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 84 ) state = 10;
else state = -1;
match = 10;
match_pos = pos;
break;
case 19:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 77 ) || ( info.src.charCodeAt( pos ) >= 79 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 78 ) state = 16;
else state = -1;
match = 10;
match_pos = pos;
break;
case 20:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 78 ) || ( info.src.charCodeAt( pos ) >= 80 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 79 ) state = 18;
else state = -1;
match = 10;
match_pos = pos;
break;
}
pos++;
}
while( state > -1 );
}
while( 1 > -1 && match == 1 );
if( match > -1 )
{
info.att = info.src.substr( start, match_pos - start );
info.offset = match_pos;
}
else
{
info.att = new String();
match = -1;
}
return match;
}
function __NODEJS_parse( src, err_off, err_la )
{
var sstack = new Array();
var vstack = new Array();
var err_cnt = 0;
var act;
var go;
var la;
var rval;
var parseinfo = new Function( "", "var offset; var src; var att;" );
var info = new parseinfo();
/* Pop-Table */
var pop_tab = new Array(
new Array( 0/* begin' */, 1 ),
new Array( 13/* begin */, 1 ),
new Array( 12/* search_text */, 1 ),
new Array( 12/* search_text */, 2 ),
new Array( 12/* search_text */, 3 ),
new Array( 14/* and_expression */, 1 ),
new Array( 14/* and_expression */, 3 ),
new Array( 15/* boolean_expression */, 2 ),
new Array( 15/* boolean_expression */, 1 ),
new Array( 16/* expression */, 3 ),
new Array( 16/* expression */, 2 ),
new Array( 16/* expression */, 1 ),
new Array( 17/* value */, 2 ),
new Array( 17/* value */, 1 ),
new Array( 18/* string */, 1 ),
new Array( 18/* string */, 1 )
);
/* Action-Table */
var act_tab = new Array(
/* State 0 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 1 */ new Array( 19/* "$" */,0 ),
/* State 2 */ new Array( 19/* "$" */,-1 ),
/* State 3 */ new Array( 6/* "OR" */,14 , 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 , 19/* "$" */,-2 , 4/* "RIGHT_PARENTHESE" */,-2 ),
/* State 4 */ new Array( 5/* "AND" */,16 , 19/* "$" */,-5 , 7/* "NOT" */,-5 , 3/* "LEFT_PARENTHESE" */,-5 , 8/* "COLUMN" */,-5 , 11/* "OPERATOR" */,-5 , 10/* "WORD" */,-5 , 9/* "STRING" */,-5 , 6/* "OR" */,-5 , 4/* "RIGHT_PARENTHESE" */,-5 ),
/* State 5 */ new Array( 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 6 */ new Array( 19/* "$" */,-8 , 7/* "NOT" */,-8 , 3/* "LEFT_PARENTHESE" */,-8 , 8/* "COLUMN" */,-8 , 11/* "OPERATOR" */,-8 , 10/* "WORD" */,-8 , 9/* "STRING" */,-8 , 6/* "OR" */,-8 , 5/* "AND" */,-8 , 4/* "RIGHT_PARENTHESE" */,-8 ),
/* State 7 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 8 */ new Array( 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 9 */ new Array( 19/* "$" */,-11 , 7/* "NOT" */,-11 , 3/* "LEFT_PARENTHESE" */,-11 , 8/* "COLUMN" */,-11 , 11/* "OPERATOR" */,-11 , 10/* "WORD" */,-11 , 9/* "STRING" */,-11 , 6/* "OR" */,-11 , 5/* "AND" */,-11 , 4/* "RIGHT_PARENTHESE" */,-11 ),
/* State 10 */ new Array( 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 11 */ new Array( 19/* "$" */,-13 , 7/* "NOT" */,-13 , 3/* "LEFT_PARENTHESE" */,-13 , 8/* "COLUMN" */,-13 , 11/* "OPERATOR" */,-13 , 10/* "WORD" */,-13 , 9/* "STRING" */,-13 , 6/* "OR" */,-13 , 5/* "AND" */,-13 , 4/* "RIGHT_PARENTHESE" */,-13 ),
/* State 12 */ new Array( 19/* "$" */,-14 , 7/* "NOT" */,-14 , 3/* "LEFT_PARENTHESE" */,-14 , 8/* "COLUMN" */,-14 , 11/* "OPERATOR" */,-14 , 10/* "WORD" */,-14 , 9/* "STRING" */,-14 , 6/* "OR" */,-14 , 5/* "AND" */,-14 , 4/* "RIGHT_PARENTHESE" */,-14 ),
/* State 13 */ new Array( 19/* "$" */,-15 , 7/* "NOT" */,-15 , 3/* "LEFT_PARENTHESE" */,-15 , 8/* "COLUMN" */,-15 , 11/* "OPERATOR" */,-15 , 10/* "WORD" */,-15 , 9/* "STRING" */,-15 , 6/* "OR" */,-15 , 5/* "AND" */,-15 , 4/* "RIGHT_PARENTHESE" */,-15 ),
/* State 14 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 15 */ new Array( 19/* "$" */,-3 , 4/* "RIGHT_PARENTHESE" */,-3 ),
/* State 16 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 17 */ new Array( 19/* "$" */,-7 , 7/* "NOT" */,-7 , 3/* "LEFT_PARENTHESE" */,-7 , 8/* "COLUMN" */,-7 , 11/* "OPERATOR" */,-7 , 10/* "WORD" */,-7 , 9/* "STRING" */,-7 , 6/* "OR" */,-7 , 5/* "AND" */,-7 , 4/* "RIGHT_PARENTHESE" */,-7 ),
/* State 18 */ new Array( 4/* "RIGHT_PARENTHESE" */,23 ),
/* State 19 */ new Array( 19/* "$" */,-10 , 7/* "NOT" */,-10 , 3/* "LEFT_PARENTHESE" */,-10 , 8/* "COLUMN" */,-10 , 11/* "OPERATOR" */,-10 , 10/* "WORD" */,-10 , 9/* "STRING" */,-10 , 6/* "OR" */,-10 , 5/* "AND" */,-10 , 4/* "RIGHT_PARENTHESE" */,-10 ),
/* State 20 */ new Array( 19/* "$" */,-12 , 7/* "NOT" */,-12 , 3/* "LEFT_PARENTHESE" */,-12 , 8/* "COLUMN" */,-12 , 11/* "OPERATOR" */,-12 , 10/* "WORD" */,-12 , 9/* "STRING" */,-12 , 6/* "OR" */,-12 , 5/* "AND" */,-12 , 4/* "RIGHT_PARENTHESE" */,-12 ),
/* State 21 */ new Array( 19/* "$" */,-4 , 4/* "RIGHT_PARENTHESE" */,-4 ),
/* State 22 */ new Array( 19/* "$" */,-6 , 7/* "NOT" */,-6 , 3/* "LEFT_PARENTHESE" */,-6 , 8/* "COLUMN" */,-6 , 11/* "OPERATOR" */,-6 , 10/* "WORD" */,-6 , 9/* "STRING" */,-6 , 6/* "OR" */,-6 , 4/* "RIGHT_PARENTHESE" */,-6 ),
/* State 23 */ new Array( 19/* "$" */,-9 , 7/* "NOT" */,-9 , 3/* "LEFT_PARENTHESE" */,-9 , 8/* "COLUMN" */,-9 , 11/* "OPERATOR" */,-9 , 10/* "WORD" */,-9 , 9/* "STRING" */,-9 , 6/* "OR" */,-9 , 5/* "AND" */,-9 , 4/* "RIGHT_PARENTHESE" */,-9 )
);
/* Goto-Table */
var goto_tab = new Array(
/* State 0 */ new Array( 13/* begin */,1 , 12/* search_text */,2 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 1 */ new Array( ),
/* State 2 */ new Array( ),
/* State 3 */ new Array( 12/* search_text */,15 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 4 */ new Array( ),
/* State 5 */ new Array( 16/* expression */,17 , 17/* value */,9 , 18/* string */,11 ),
/* State 6 */ new Array( ),
/* State 7 */ new Array( 12/* search_text */,18 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 8 */ new Array( 16/* expression */,19 , 17/* value */,9 , 18/* string */,11 ),
/* State 9 */ new Array( ),
/* State 10 */ new Array( 18/* string */,20 ),
/* State 11 */ new Array( ),
/* State 12 */ new Array( ),
/* State 13 */ new Array( ),
/* State 14 */ new Array( 12/* search_text */,21 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 15 */ new Array( ),
/* State 16 */ new Array( 14/* and_expression */,22 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 17 */ new Array( ),
/* State 18 */ new Array( ),
/* State 19 */ new Array( ),
/* State 20 */ new Array( ),
/* State 21 */ new Array( ),
/* State 22 */ new Array( ),
/* State 23 */ new Array( )
);
/* Symbol labels */
var labels = new Array(
"begin'" /* Non-terminal symbol */,
"WHITESPACE" /* Terminal symbol */,
"WHITESPACE" /* Terminal symbol */,
"LEFT_PARENTHESE" /* Terminal symbol */,
"RIGHT_PARENTHESE" /* Terminal symbol */,
"AND" /* Terminal symbol */,
"OR" /* Terminal symbol */,
"NOT" /* Terminal symbol */,
"COLUMN" /* Terminal symbol */,
"STRING" /* Terminal symbol */,
"WORD" /* Terminal symbol */,
"OPERATOR" /* Terminal symbol */,
"search_text" /* Non-terminal symbol */,
"begin" /* Non-terminal symbol */,
"and_expression" /* Non-terminal symbol */,
"boolean_expression" /* Non-terminal symbol */,
"expression" /* Non-terminal symbol */,
"value" /* Non-terminal symbol */,
"string" /* Non-terminal symbol */,
"$" /* Terminal symbol */
);
info.offset = 0;
info.src = src;
info.att = new String();
if( !err_off )
err_off = new Array();
if( !err_la )
err_la = new Array();
sstack.push( 0 );
vstack.push( 0 );
la = __NODEJS_lex( info );
while( true )
{
act = 25;
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( act_tab[sstack[sstack.length-1]][i] == la )
{
act = act_tab[sstack[sstack.length-1]][i+1];
break;
}
}
if( NODEJS__dbg_withtrace && sstack.length > 0 )
{
__NODEJS_dbg_print( "\nState " + sstack[sstack.length-1] + "\n" +
"\tLookahead: " + labels[la] + " (\"" + info.att + "\")\n" +
"\tAction: " + act + "\n" +
"\tSource: \"" + info.src.substr( info.offset, 30 ) + ( ( info.offset + 30 < info.src.length ) ?
"..." : "" ) + "\"\n" +
"\tStack: " + sstack.join() + "\n" +
"\tValue stack: " + vstack.join() + "\n" );
}
//Panic-mode: Try recovery when parse-error occurs!
if( act == 25 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "Error detected: There is no reduce or shift on the symbol " + labels[la] );
err_cnt++;
err_off.push( info.offset - info.att.length );
err_la.push( new Array() );
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
err_la[err_la.length-1].push( labels[act_tab[sstack[sstack.length-1]][i]] );
//Remember the original stack!
var rsstack = new Array();
var rvstack = new Array();
for( var i = 0; i < sstack.length; i++ )
{
rsstack[i] = sstack[i];
rvstack[i] = vstack[i];
}
while( act == 25 && la != 19 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tError recovery\n" +
"Current lookahead: " + labels[la] + " (" + info.att + ")\n" +
"Action: " + act + "\n\n" );
if( la == -1 )
info.offset++;
while( act == 25 && sstack.length > 0 )
{
sstack.pop();
vstack.pop();
if( sstack.length == 0 )
break;
act = 25;
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( act_tab[sstack[sstack.length-1]][i] == la )
{
act = act_tab[sstack[sstack.length-1]][i+1];
break;
}
}
}
if( act != 25 )
break;
for( var i = 0; i < rsstack.length; i++ )
{
sstack.push( rsstack[i] );
vstack.push( rvstack[i] );
}
la = __NODEJS_lex( info );
}
if( act == 25 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tError recovery failed, terminating parse process..." );
break;
}
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tError recovery succeeded, continuing" );
}
/*
if( act == 25 )
break;
*/
//Shift
if( act > 0 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "Shifting symbol: " + labels[la] + " (" + info.att + ")" );
sstack.push( act );
vstack.push( info.att );
la = __NODEJS_lex( info );
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tNew lookahead symbol: " + labels[la] + " (" + info.att + ")" );
}
//Reduce
else
{
act *= -1;
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "Reducing by producution: " + act );
rval = void(0);
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tPerforming semantic action..." );
switch( act )
{
case 0:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 1:
{
result = vstack[ vstack.length - 1 ];
}
break;
case 2:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 3:
{
rval = mkComplexQuery('OR',[vstack[ vstack.length - 2 ],vstack[ vstack.length - 1 ]]);
}
break;
case 4:
{
rval = mkComplexQuery('OR',[vstack[ vstack.length - 3 ],vstack[ vstack.length - 1 ]]);
}
break;
case 5:
{
rval = vstack[ vstack.length - 1 ] ;
}
break;
case 6:
{
rval = mkComplexQuery('AND',[vstack[ vstack.length - 3 ],vstack[ vstack.length - 1 ]]);
}
break;
case 7:
{
rval = mkNotQuery(vstack[ vstack.length - 1 ]);
}
break;
case 8:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 9:
{
rval = vstack[ vstack.length - 2 ];
}
break;
case 10:
{
simpleQuerySetKey(vstack[ vstack.length - 1 ],vstack[ vstack.length - 2 ].split(':').slice(0,-1).join(':')); rval = vstack[ vstack.length - 1 ];
}
break;
case 11:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 12:
{
vstack[ vstack.length - 1 ].operator = vstack[ vstack.length - 2 ] ; rval = vstack[ vstack.length - 1 ];
}
break;
case 13:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 14:
{
rval = mkSimpleQuery('',vstack[ vstack.length - 1 ]);
}
break;
case 15:
{
rval = mkSimpleQuery('',vstack[ vstack.length - 1 ].split('"').slice(1,-1).join('"'));
}
break;
}
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tPopping " + pop_tab[act][1] + " off the stack..." );
for( var i = 0; i < pop_tab[act][1]; i++ )
{
sstack.pop();
vstack.pop();
}
go = -1;
for( var i = 0; i < goto_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( goto_tab[sstack[sstack.length-1]][i] == pop_tab[act][0] )
{
go = goto_tab[sstack[sstack.length-1]][i+1];
break;
}
}
if( act == 0 )
break;
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tPushing non-terminal " + labels[ pop_tab[act][0] ] );
sstack.push( go );
vstack.push( rval );
}
if( NODEJS__dbg_withtrace )
{
alert( NODEJS__dbg_string );
NODEJS__dbg_string = new String();
}
}
if( NODEJS__dbg_withtrace )
{
__NODEJS_dbg_print( "\nParse complete." );
alert( NODEJS__dbg_string );
}
return err_cnt;
}
var arrayExtend = function () {
var j, i, newlist = [], list_list = arguments;
for (j = 0; j < list_list.length; j += 1) {
for (i = 0; i < list_list[j].length; i += 1) {
newlist.push(list_list[j][i]);
}
}
return newlist;
}, mkSimpleQuery = function (key, value, operator) {
var object = {"type": "simple", "key": key, "value": value};
if (operator !== undefined) {
object.operator = operator;
}
return object;
}, mkNotQuery = function (query) {
if (query.operator === "NOT") {
return query.query_list[0];
}
return {"type": "complex", "operator": "NOT", "query_list": [query]};
}, mkComplexQuery = function (operator, query_list) {
var i, query_list2 = [];
for (i = 0; i < query_list.length; i += 1) {
if (query_list[i].operator === operator) {
query_list2 = arrayExtend(query_list2, query_list[i].query_list);
} else {
query_list2.push(query_list[i]);
}
}
return {type:"complex",operator:operator,query_list:query_list2};
}, simpleQuerySetKey = function (query, key) {
var i;
if (query.type === "complex") {
for (i = 0; i < query.query_list.length; ++i) {
simpleQuerySetKey (query.query_list[i],key);
}
return true;
}
if (query.type === "simple" && !query.key) {
query.key = key;
return true;
}
return false;
},
error_offsets = [],
error_lookaheads = [],
error_count = 0,
result;
if ((error_count = __NODEJS_parse(string, error_offsets, error_lookaheads)) > 0) {
var i;
for (i = 0; i < error_count; i += 1) {
throw new Error("Parse error near \"" +
string.substr(error_offsets[i]) +
"\", expecting \"" +
error_lookaheads[i].join() + "\"");
}
}
return result;
} // parseStringToObject
Query.parseStringToObject = parseStringToObject;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query: true, query_class_dict: true, inherits: true,
exports, QueryFactory, RSVP, sequence */
/**
* The ComplexQuery inherits from Query, and compares one or several metadata
* values.
*
* @class ComplexQuery
* @extends Query
* @param {Object} [spec={}] The specifications
* @param {String} [spec.operator="AND"] The compare method to use
* @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare
*/
function ComplexQuery(spec, key_schema) {
Query.call(this);
/**
* Logical operator to use to compare object values
*
* @attribute operator
* @type String
* @default "AND"
* @optional
*/
this.operator = spec.operator;
/**
* The sub Query list which are used to query an item.
*
* @attribute query_list
* @type Array
* @default []
* @optional
*/
this.query_list = spec.query_list || [];
/*jslint unparam: true*/
this.query_list = this.query_list.map(
// decorate the map to avoid sending the index as key_schema argument
function (o, i) { return QueryFactory.create(o, key_schema); }
);
/*jslint unparam: false*/
}
inherits(ComplexQuery, Query);
ComplexQuery.prototype.operator = "AND";
ComplexQuery.prototype.type = "complex";
/**
* #crossLink "Query/match:method"
*/
ComplexQuery.prototype.match = function (item) {
var operator = this.operator;
if (!(/^(?:AND|OR|NOT)$/i.test(operator))) {
operator = "AND";
}
return this[operator.toUpperCase()](item);
};
/**
* #crossLink "Query/toString:method"
*/
ComplexQuery.prototype.toString = function () {
var str_list = [], this_operator = this.operator;
if (this.operator === "NOT") {
str_list.push("NOT (");
str_list.push(this.query_list[0].toString());
str_list.push(")");
return str_list.join(" ");
}
this.query_list.forEach(function (query) {
str_list.push("(");
str_list.push(query.toString());
str_list.push(")");
str_list.push(this_operator);
});
str_list.length -= 1;
return str_list.join(" ");
};
/**
* #crossLink "Query/serialized:method"
*/
ComplexQuery.prototype.serialized = function () {
var s = {
"type": "complex",
"operator": this.operator,
"query_list": []
};
this.query_list.forEach(function (query) {
s.query_list.push(
typeof query.toJSON === "function" ? query.toJSON() : query
);
});
return s;
};
ComplexQuery.prototype.toJSON = ComplexQuery.prototype.serialized;
/**
* Comparison operator, test if all sub queries match the
* item value
*
* @method AND
* @param {Object} item The item to match
* @return {Boolean} true if all match, false otherwise
*/
ComplexQuery.prototype.AND = function (item) {
var j, promises = [];
for (j = 0; j < this.query_list.length; j += 1) {
promises.push(this.query_list[j].match(item));
}
function cancel() {
var i;
for (i = 0; i < promises.length; i += 1) {
if (typeof promises.cancel === 'function') {
promises.cancel();
}
}
}
return new RSVP.Promise(function (resolve, reject) {
var i, count = 0;
function resolver(value) {
if (!value) {
resolve(false);
}
count += 1;
if (count === promises.length) {
resolve(true);
}
}
function rejecter(err) {
reject(err);
cancel();
}
for (i = 0; i < promises.length; i += 1) {
promises[i].then(resolver, rejecter);
}
}, cancel);
};
/**
* Comparison operator, test if one of the sub queries matches the
* item value
*
* @method OR
* @param {Object} item The item to match
* @return {Boolean} true if one match, false otherwise
*/
ComplexQuery.prototype.OR = function (item) {
var j, promises = [];
for (j = 0; j < this.query_list.length; j += 1) {
promises.push(this.query_list[j].match(item));
}
function cancel() {
var i;
for (i = 0; i < promises.length; i += 1) {
if (typeof promises.cancel === 'function') {
promises.cancel();
}
}
}
return new RSVP.Promise(function (resolve, reject) {
var i, count = 0;
function resolver(value) {
if (value) {
resolve(true);
}
count += 1;
if (count === promises.length) {
resolve(false);
}
}
function rejecter(err) {
reject(err);
cancel();
}
for (i = 0; i < promises.length; i += 1) {
promises[i].then(resolver, rejecter);
}
}, cancel);
};
/**
* Comparison operator, test if the sub query does not match the
* item value
*
* @method NOT
* @param {Object} item The item to match
* @return {Boolean} true if one match, false otherwise
*/
ComplexQuery.prototype.NOT = function (item) {
return sequence([function () {
return this.query_list[0].match(item);
}, function (answer) {
return !answer;
}]);
};
query_class_dict.complex = ComplexQuery;
exports.ComplexQuery = ComplexQuery;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global exports, ComplexQuery, SimpleQuery, Query, parseStringToObject,
query_class_dict */
/**
* Provides static methods to create Query object
*
* @class QueryFactory
*/
function QueryFactory() {
return;
}
/**
* Creates Query object from a search text string or a serialized version
* of a Query.
*
* @method create
* @static
* @param {Object,String} object The search text or the serialized version
* of a Query
* @return {Query} A Query object
*/
QueryFactory.create = function (object, key_schema) {
if (object === "") {
return new Query();
}
if (typeof object === "string") {
object = parseStringToObject(object);
}
if (typeof (object || {}).type === "string" &&
query_class_dict[object.type]) {
return new query_class_dict[object.type](object, key_schema);
}
throw new TypeError("QueryFactory.create(): " +
"Argument 1 is not a search text or a parsable object");
};
exports.QueryFactory = QueryFactory;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query, exports */
function objectToSearchText(query) {
var str_list = [];
if (query.type === "complex") {
str_list.push("(");
(query.query_list || []).forEach(function (sub_query) {
str_list.push(objectToSearchText(sub_query));
str_list.push(query.operator);
});
str_list.length -= 1;
str_list.push(")");
return str_list.join(" ");
}
if (query.type === "simple") {
return (query.key ? query.key + ": " : "") +
(query.operator || "") + ' "' + query.value + '"';
}
throw new TypeError("This object is not a query");
}
Query.objectToSearchText = objectToSearchText;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query, inherits, query_class_dict, exports,
searchTextToRegExp, RSVP */
var checkKeySchema = function (key_schema) {
var prop;
if (key_schema !== undefined) {
if (typeof key_schema !== 'object') {
throw new TypeError("SimpleQuery().create(): " +
"key_schema is not of type 'object'");
}
// key_set is mandatory
if (key_schema.key_set === undefined) {
throw new TypeError("SimpleQuery().create(): " +
"key_schema has no 'key_set' property");
}
for (prop in key_schema) {
if (key_schema.hasOwnProperty(prop)) {
switch (prop) {
case 'key_set':
case 'cast_lookup':
case 'match_lookup':
break;
default:
throw new TypeError("SimpleQuery().create(): " +
"key_schema has unknown property '" + prop + "'");
}
}
}
}
};
/**
* The SimpleQuery inherits from Query, and compares one metadata value
*
* @class SimpleQuery
* @extends Query
* @param {Object} [spec={}] The specifications
* @param {String} [spec.operator="="] The compare method to use
* @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare
*/
function SimpleQuery(spec, key_schema) {
Query.call(this);
checkKeySchema(key_schema);
this._key_schema = key_schema || {};
/**
* Operator to use to compare object values
*
* @attribute operator
* @type String
* @optional
*/
this.operator = spec.operator;
/**
* Key of the object which refers to the value to compare
*
* @attribute key
* @type String
*/
this.key = spec.key;
/**
* Value is used to do the comparison with the object value
*
* @attribute value
* @type String
*/
this.value = spec.value;
}
inherits(SimpleQuery, Query);
SimpleQuery.prototype.type = "simple";
var checkKey = function (key) {
var prop;
if (key.read_from === undefined) {
throw new TypeError("Custom key is missing the read_from property");
}
for (prop in key) {
if (key.hasOwnProperty(prop)) {
switch (prop) {
case 'read_from':
case 'cast_to':
case 'equal_match':
break;
default:
throw new TypeError("Custom key has unknown property '" +
prop + "'");
}
}
}
};
/**
* #crossLink "Query/match:method"
*/
SimpleQuery.prototype.match = function (item) {
var object_value = null,
equal_match = null,
cast_to = null,
matchMethod = null,
operator = this.operator,
value = null,
key = this.key;
/*jslint regexp: true */
if (!(/^(?:!?=|<=?|>=?)$/i.test(operator))) {
// `operator` is not correct, we have to change it to "like" or "="
if (/%/.test(this.value)) {
// `value` contains a non escaped `%`
operator = "like";
} else {
// `value` does not contain non escaped `%`
operator = "=";
}
}
matchMethod = this[operator];
if (this._key_schema.key_set && this._key_schema.key_set[key] !== undefined) {
key = this._key_schema.key_set[key];
}
if (typeof key === 'object') {
checkKey(key);
object_value = item[key.read_from];
equal_match = key.equal_match;
// equal_match can be a string
if (typeof equal_match === 'string') {
// XXX raise error if equal_match not in match_lookup
equal_match = this._key_schema.match_lookup[equal_match];
}
// equal_match overrides the default '=' operator
if (equal_match !== undefined) {
matchMethod = (operator === "=" || operator === "like" ?
equal_match : matchMethod);
}
value = this.value;
cast_to = key.cast_to;
if (cast_to) {
// cast_to can be a string
if (typeof cast_to === 'string') {
// XXX raise error if cast_to not in cast_lookup
cast_to = this._key_schema.cast_lookup[cast_to];
}
try {
value = cast_to(value);
} catch (e) {
value = undefined;
}
try {
object_value = cast_to(object_value);
} catch (e) {
object_value = undefined;
}
}
} else {
object_value = item[key];
value = this.value;
}
if (object_value === undefined || value === undefined) {
return RSVP.resolve(false);
}
return matchMethod(object_value, value);
};
/**
* #crossLink "Query/toString:method"
*/
SimpleQuery.prototype.toString = function () {
return (this.key ? this.key + ":" : "") +
(this.operator ? " " + this.operator : "") + ' "' + this.value + '"';
};
/**
* #crossLink "Query/serialized:method"
*/
SimpleQuery.prototype.serialized = function () {
var object = {
"type": "simple",
"key": this.key,
"value": this.value
};
if (this.operator !== undefined) {
object.operator = this.operator;
}
return object;
};
SimpleQuery.prototype.toJSON = SimpleQuery.prototype.serialized;
/**
* Comparison operator, test if this query value matches the item value
*
* @method =
* @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value
* @return {Boolean} true if match, false otherwise
*/
SimpleQuery.prototype["="] = function (object_value, comparison_value) {
var value, i;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
for (i = 0; i < object_value.length; i += 1) {
value = object_value[i];
if (typeof value === 'object' && value.hasOwnProperty('content')) {
value = value.content;
}
if (typeof value.cmp === "function") {
return RSVP.resolve(value.cmp(comparison_value) === 0);
}
if (
searchTextToRegExp(comparison_value.toString(), false).
test(value.toString())
) {
return RSVP.resolve(true);
}
}
return RSVP.resolve(false);
};
/**
* Comparison operator, test if this query value matches the item value
*
* @method like
* @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value
* @return {Boolean} true if match, false otherwise
*/
SimpleQuery.prototype.like = function (object_value, comparison_value) {
var value, i;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
for (i = 0; i < object_value.length; i += 1) {
value = object_value[i];
if (typeof value === 'object' && value.hasOwnProperty('content')) {
value = value.content;
}
if (typeof value.cmp === "function") {
return RSVP.resolve(value.cmp(comparison_value) === 0);
}
if (
searchTextToRegExp(comparison_value.toString()).test(value.toString())
) {
return RSVP.resolve(true);
}
}
return RSVP.resolve(false);
};
/**
* Comparison operator, test if this query value does not match the item value
*
* @method !=
* @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value
* @return {Boolean} true if not match, false otherwise
*/
SimpleQuery.prototype["!="] = function (object_value, comparison_value) {
var value, i;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
for (i = 0; i < object_value.length; i += 1) {
value = object_value[i];
if (typeof value === 'object' && value.hasOwnProperty('content')) {
value = value.content;
}
if (typeof value.cmp === "function") {
return RSVP.resolve(value.cmp(comparison_value) !== 0);
}
if (
searchTextToRegExp(comparison_value.toString(), false).
test(value.toString())
) {
return RSVP.resolve(false);
}
}
return RSVP.resolve(true);
};
/**
* Comparison operator, test if this query value is lower than the item value
*
* @method <
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if lower, false otherwise
*/
SimpleQuery.prototype["<"] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object' && value.hasOwnProperty('content')) {
value = value.content;
}
if (typeof value.cmp === "function") {
return RSVP.resolve(value.cmp(comparison_value) < 0);
}
return RSVP.resolve(value < comparison_value);
};
/**
* Comparison operator, test if this query value is equal or lower than the
* item value
*
* @method <=
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or lower, false otherwise
*/
SimpleQuery.prototype["<="] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object' && value.hasOwnProperty('content')) {
value = value.content;
}
if (typeof value.cmp === "function") {
return RSVP.resolve(value.cmp(comparison_value) <= 0);
}
return RSVP.resolve(value <= comparison_value);
};
/**
* Comparison operator, test if this query value is greater than the item
* value
*
* @method >
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if greater, false otherwise
*/
SimpleQuery.prototype[">"] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object' && value.hasOwnProperty('content')) {
value = value.content;
}
if (typeof value.cmp === "function") {
return RSVP.resolve(value.cmp(comparison_value) > 0);
}
return RSVP.resolve(value > comparison_value);
};
/**
* Comparison operator, test if this query value is equal or greater than the
* item value
*
* @method >=
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or greater, false otherwise
*/
SimpleQuery.prototype[">="] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object' && value.hasOwnProperty('content')) {
value = value.content;
}
if (typeof value.cmp === "function") {
return RSVP.resolve(value.cmp(comparison_value) >= 0);
}
return RSVP.resolve(value >= comparison_value);
};
query_class_dict.simple = SimpleQuery;
exports.SimpleQuery = SimpleQuery;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query, RSVP, deepClone */
/**
* Escapes regexp special chars from a string.
*
* @param {String} string The string to escape
* @return {String} The escaped string
*/
function stringEscapeRegexpCharacters(string) {
if (typeof string === "string") {
return string.replace(/([\\\.\$\[\]\(\)\{\}\^\?\*\+\-])/g, "\\$1");
}
throw new TypeError("Query.stringEscapeRegexpCharacters(): " +
"Argument no 1 is not of type 'string'");
}
Query.stringEscapeRegexpCharacters = stringEscapeRegexpCharacters;
/**
* Convert metadata values to array of strings. ex:
*
* "a" -> ["a"],
* {"content": "a"} -> ["a"]
*
* @param {Any} value The metadata value
* @return {Array} The value in string array format
*/
function metadataValueToStringArray(value) {
var i, new_value = [];
if (value === undefined) {
return undefined;
}
if (!Array.isArray(value)) {
value = [value];
}
for (i = 0; i < value.length; i += 1) {
if (typeof value[i] === 'object') {
new_value[i] = value[i].content;
} else {
new_value[i] = value[i];
}
}
return new_value;
}
/**
* A sort function to sort items by key
*
* @param {String} key The key to sort on
* @param {String} [way="ascending"] 'ascending' or 'descending'
* @return {Function} The sort function
*/
function sortFunction(key, way) {
if (way === 'descending') {
return function (a, b) {
// this comparison is 5 times faster than json comparison
var i, l;
a = metadataValueToStringArray(a[key]) || [];
b = metadataValueToStringArray(b[key]) || [];
l = a.length > b.length ? a.length : b.length;
for (i = 0; i < l; i += 1) {
if (a[i] === undefined) {
return 1;
}
if (b[i] === undefined) {
return -1;
}
if (a[i] > b[i]) {
return -1;
}
if (a[i] < b[i]) {
return 1;
}
}
return 0;
};
}
if (way === 'ascending') {
return function (a, b) {
// this comparison is 5 times faster than json comparison
var i, l;
a = metadataValueToStringArray(a[key]) || [];
b = metadataValueToStringArray(b[key]) || [];
l = a.length > b.length ? a.length : b.length;
for (i = 0; i < l; i += 1) {
if (a[i] === undefined) {
return -1;
}
if (b[i] === undefined) {
return 1;
}
if (a[i] > b[i]) {
return 1;
}
if (a[i] < b[i]) {
return -1;
}
}
return 0;
};
}
throw new TypeError("Query.sortFunction(): " +
"Argument 2 must be 'ascending' or 'descending'");
}
/**
* Inherits the prototype methods from one constructor into another. The
* prototype of `constructor` will be set to a new object created from
* `superConstructor`.
*
* @param {Function} constructor The constructor which inherits the super one
* @param {Function} superConstructor The super constructor
*/
function inherits(constructor, superConstructor) {
constructor.super_ = superConstructor;
constructor.prototype = Object.create(superConstructor.prototype, {
"constructor": {
"configurable": true,
"enumerable": false,
"writable": true,
"value": constructor
}
});
}
/**
* Does nothing
*/
function emptyFunction() {
return;
}
/**
* Filter a list of items, modifying them to select only wanted keys. If
* `clone` is true, then the method will act on a cloned list.
*
* @param {Array} select_option Key list to keep
* @param {Array} list The item list to filter
* @param {Boolean} [clone=false] If true, modifies a clone of the list
* @return {Array} The filtered list
*/
function select(select_option, list, clone) {
var i, j, new_item;
if (!Array.isArray(select_option)) {
throw new TypeError("jioquery.select(): " +
"Argument 1 is not of type Array");
}
if (!Array.isArray(list)) {
throw new TypeError("jioquery.select(): " +
"Argument 2 is not of type Array");
}
if (clone === true) {
list = deepClone(list);
}
for (i = 0; i < list.length; i += 1) {
new_item = {};
for (j = 0; j < select_option.length; j += 1) {
if (list[i].hasOwnProperty([select_option[j]])) {
new_item[select_option[j]] = list[i][select_option[j]];
}
}
for (j in new_item) {
if (new_item.hasOwnProperty(j)) {
list[i] = new_item;
break;
}
}
}
return list;
}
Query.select = select;
/**
* Sort a list of items, according to keys and directions. If `clone` is true,
* then the method will act on a cloned list.
*
* @param {Array} sort_on_option List of couples [key, direction]
* @param {Array} list The item list to sort
* @param {Boolean} [clone=false] If true, modifies a clone of the list
* @return {Array} The filtered list
*/
function sortOn(sort_on_option, list, clone) {
var sort_index;
if (!Array.isArray(sort_on_option)) {
throw new TypeError("jioquery.sortOn(): " +
"Argument 1 is not of type 'array'");
}
if (clone) {
list = deepClone(list);
}
for (sort_index = sort_on_option.length - 1; sort_index >= 0;
sort_index -= 1) {
list.sort(sortFunction(
sort_on_option[sort_index][0],
sort_on_option[sort_index][1]
));
}
return list;
}
Query.sortOn = sortOn;
/**
* Limit a list of items, according to index and length. If `clone` is true,
* then the method will act on a cloned list.
*
* @param {Array} limit_option A couple [from, length]
* @param {Array} list The item list to limit
* @param {Boolean} [clone=false] If true, modifies a clone of the list
* @return {Array} The filtered list
*/
function limit(limit_option, list, clone) {
if (!Array.isArray(limit_option)) {
throw new TypeError("jioquery.limit(): " +
"Argument 1 is not of type 'array'");
}
if (!Array.isArray(list)) {
throw new TypeError("jioquery.limit(): " +
"Argument 2 is not of type 'array'");
}
if (clone) {
list = deepClone(list);
}
list.splice(0, limit_option[0]);
if (limit_option[1]) {
list.splice(limit_option[1]);
}
return list;
}
Query.limit = limit;
/**
* Convert a search text to a regexp.
*
* @param {String} string The string to convert
* @param {Boolean} [use_wildcard_character=true] Use wildcard "%" and "_"
* @return {RegExp} The search text regexp
*/
function searchTextToRegExp(string, use_wildcard_characters) {
if (typeof string !== 'string') {
throw new TypeError("jioquery.searchTextToRegExp(): " +
"Argument 1 is not of type 'string'");
}
if (use_wildcard_characters === false) {
return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$");
}
return new RegExp("^" + stringEscapeRegexpCharacters(string).replace(
/%/g,
".*"
).replace(
/_/g,
"."
) + "$");
}
Query.searchTextToRegExp = searchTextToRegExp;
/**
* sequence(thens): Promise
*
* Executes a sequence of *then* callbacks. It acts like
* `smth().then(callback).then(callback)...`. The first callback is called with
* no parameter.
*
* Elements of `thens` array can be a function or an array contaning at most
* three *then* callbacks: *onFulfilled*, *onRejected*, *onNotified*.
*
* When `cancel()` is executed, each then promises are cancelled at the same
* time.
*
* @param {Array} thens An array of *then* callbacks
* @return {Promise} A new promise
*/
function sequence(thens) {
var promises = [];
return new RSVP.Promise(function (resolve, reject, notify) {
var i;
promises[0] = new RSVP.Promise(function (resolve) {
resolve();
});
for (i = 0; i < thens.length; i += 1) {
if (Array.isArray(thens[i])) {
promises[i + 1] = promises[i].
then(thens[i][0], thens[i][1], thens[i][2]);
} else {
promises[i + 1] = promises[i].then(thens[i]);
}
}
promises[i].then(resolve, reject, notify);
}, function () {
var i;
for (i = 0; i < promises.length; i += 1) {
promises[i].cancel();
}
});
}
}));
......@@ -5,7 +5,7 @@
*/
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global jIO, localStorage, setTimeout, complex_queries, window, define,
/*global jIO, localStorage, setTimeout, window, define, Blob, Uint8Array,
exports, require */
/**
......@@ -54,15 +54,14 @@
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports, require('jio'), require('complex_queries'));
return module(exports, require('jio'));
}
window.local_storage = {};
module(window.local_storage, jIO, complex_queries);
module(window.local_storage, jIO);
}([
'exports',
'jio',
'complex_queries'
], function (exports, jIO, complex_queries) {
'jio'
], function (exports, jIO) {
"use strict";
/**
......@@ -124,9 +123,8 @@
* @constructor
*/
function LocalStorage(spec) {
if (typeof spec.username !== 'string' && !spec.username) {
throw new TypeError("LocalStorage 'username' must be a string " +
"which contains more than one character.");
if (typeof spec.username !== 'string' || spec.username === '') {
throw new TypeError("LocalStorage 'username' must be a non-empty string");
}
this._localpath = 'jio/localstorage/' + spec.username + '/' + (
spec.application_name === null || spec.application_name ===
......@@ -142,6 +140,7 @@
this._database = localStorage;
this._storage = localstorage;
this._mode = "localStorage";
this._key_schema = spec.key_schema;
break;
}
}
......@@ -215,7 +214,7 @@
* @param {Object} options The command options
*/
LocalStorage.prototype.putAttachment = function (command, param) {
var that = this, doc, status = "ok";
var that = this, doc, status = "created";
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
// the document does not exist
......@@ -231,7 +230,7 @@
jIO.util.readBlobAsBinaryString(param._blob).then(function (e) {
doc._attachments = doc._attachments || {};
if (doc._attachments[param._attachment]) {
status = "created";
status = "no_content";
}
doc._attachments[param._attachment] = {
"content_type": param._blob.type,
......@@ -287,7 +286,7 @@
* @param {Object} options The command options
*/
LocalStorage.prototype.getAttachment = function (command, param) {
var doc;
var doc, i, uint8array, binarystring;
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
return command.error(
......@@ -306,12 +305,22 @@
);
}
// Storing data twice in binarystring and in uint8array (in memory)
// is not a problem here because localStorage <= 5MB
binarystring = this._storage.getItem(
this._localpath + "/" + param._id + "/" + param._attachment
) || "";
uint8array = new Uint8Array(binarystring.length);
for (i = 0; i < binarystring.length; i += 1) {
uint8array[i] = binarystring.charCodeAt(i); // mask `& 0xFF` not necessary
}
uint8array = new Blob([uint8array], {
"type": doc._attachments[param._attachment].content_type || ""
});
command.success({
"data": this._storage.getItem(
this._localpath + "/" + param._id +
"/" + param._attachment
) || "",
"content_type": doc._attachments[param._attachment].content_type || ""
"data": uint8array,
"digest": doc._attachments[param._attachment].digest
});
};
......@@ -362,7 +371,7 @@
*/
LocalStorage.prototype.removeAttachment = function (command, param) {
var doc = this._storage.getItem(this._localpath + "/" + param._id);
if (typeof doc !== 'object') {
if (typeof doc !== 'object' || doc === null) {
return command.error(
"not_found",
"missing document",
......@@ -402,7 +411,7 @@
rows = [];
document_list = [];
path_re = new RegExp(
"^" + complex_queries.stringEscapeRegexpCharacters(this._localpath) +
"^" + jIO.Query.stringEscapeRegexpCharacters(this._localpath) +
"/[^/]+$"
);
if (options.query === undefined && options.sort_on === undefined &&
......@@ -425,7 +434,7 @@
}
command.success({"data": {"rows": rows, "total_rows": rows.length}});
} else {
// create complex query object from returned results
// create jio query object from returned results
for (i in this._database) {
if (this._database.hasOwnProperty(i)) {
if (path_re.test(i)) {
......@@ -444,8 +453,9 @@
document_object[meta._id] = meta;
});
}
complex_queries.QueryFactory.create(options.query || "").
exec(document_list, options);
jIO.QueryFactory.create(options.query || "",
this._key_schema).
exec(document_list, options).then(function () {
document_list = document_list.map(function (value) {
var o = {
"id": value._id,
......@@ -465,6 +475,7 @@
"total_rows": document_list.length,
"rows": document_list
}});
});
}
};
......@@ -502,13 +513,13 @@
*/
LocalStorage.prototype.genericRepair = function (command, param, repair) {
var that = this, result;
var that = this, final_result;
function referenceAttachment(param, attachment) {
if (jIO.util.indexOf(param.referenced_attachments, attachment) !== -1) {
if (param.referenced_attachments.indexOf(attachment) !== -1) {
return;
}
var i = jIO.util.indexOf(param.unreferenced_attachments, attachment);
var i = param.unreferenced_attachments.indexOf(attachment);
if (i !== -1) {
param.unreferenced_attachments.splice(i, 1);
}
......@@ -517,10 +528,10 @@
}
function attachmentFound(param, attachment) {
if (jIO.util.indexOf(param.referenced_attachments, attachment) !== -1) {
if (param.referenced_attachments.indexOf(attachment) !== -1) {
return;
}
if (jIO.util.indexOf(param.unreferenced_attachments, attachment) !== -1) {
if (param.unreferenced_attachments.indexOf(attachment) !== -1) {
return;
}
param.unreferenced_attachments[param.unreferenced_attachments.length] =
......@@ -535,7 +546,7 @@
}
// check document type
if (typeof doc !== 'object') {
if (typeof doc !== 'object' || doc === null) {
// wrong document
if (!repair) {
return {"error": true, "answers": [
......@@ -656,14 +667,14 @@
param.referenced_attachments = [];
param.unreferenced_attachments = [];
if (typeof param._id === 'string') {
result = repairOne(param, repair) || {};
final_result = repairOne(param, repair) || {};
} else {
result = repairAll(param, repair) || {};
final_result = repairAll(param, repair) || {};
}
if (result.error) {
return command.error.apply(command, result.answers || []);
if (final_result.error) {
return command.error.apply(command, final_result.answers || []);
}
command.success.apply(command, result.answers || []);
command.success.apply(command, final_result.answers || []);
};
jIO.addStorage('local', LocalStorage);
......
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