Commit 5717cd1d authored by Sven Franck's avatar Sven Franck

updated to latest JIO, fixed AMD check in complex_queries.js so does not break with require

parent 7a6598b6
...@@ -28,6 +28,12 @@ var complex_queries; ...@@ -28,6 +28,12 @@ var complex_queries;
"value": value "value": value
}); });
} }
/**
* 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) { function parseStringToObject(string) {
/* /*
...@@ -714,181 +720,337 @@ if ((error_count = __NODEJS_parse(string, error_offsets, error_lookaheads)) > 0) ...@@ -714,181 +720,337 @@ if ((error_count = __NODEJS_parse(string, error_offsets, error_lookaheads)) > 0)
return result; return result;
} // parseStringToObject } // parseStringToObject
_export('parseStringToObject', parseStringToObject);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global _export: true */ /*global _export: true */
/** /**
* Create a class, manage inheritance, static methods, * Escapes regexp special chars from a string.
* protected attributes and can hide methods or/and secure methods
* *
* @param {Class} Class Classes to inherit from (0..n). The last class * @param {String} string The string to escape
* parameter will inherit from the previous one, and so on * @return {String} The escaped string
* @param {Object} option Class option (0..n)
* @param {Boolean} [option.secure_methods=false] Make methods not configurable
* and not writable
* @param {Boolean} [option.hide_methods=false] Make methods not enumerable
* @param {Boolean} [option.secure_static_methods=true] Make static methods not
* configurable and not
* writable
* @param {Boolean} [option.hide_static_methods=false] Make static methods not
* enumerable
* @param {Object} [option.static_methods={}] Object of static methods
* @param {Function} constructor The new class constructor
* @return {Class} The new class
*/ */
function newClass() { function stringEscapeRegexpCharacters(string) {
var j, k, constructors = [], option, new_class; if (typeof string === "string") {
return string.replace(/([\\\.\$\[\]\(\)\{\}\^\?\*\+\-])/g, "\\$1");
for (j = 0; j < arguments.length; j += 1) { }
if (typeof arguments[j] === "function") { throw new TypeError("complex_queries.stringEscapeRegexpCharacters(): " +
constructors.push(arguments[j]); "Argument no 1 is not of type 'string'");
} else if (typeof arguments[j] === "object") { }
option = option || {};
for (k in arguments[j]) { _export("stringEscapeRegexpCharacters", stringEscapeRegexpCharacters);
if (arguments[j].hasOwnProperty(k)) {
option[k] = arguments[j][k]; /**
} * 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;
}
function postObjectCreation(that) { /**
// modify the object according to 'option' * A sort function to sort items by key
var key; *
if (option) { * @param {String} key The key to sort on
for (key in that) { * @param {String} [way="ascending"] 'ascending' or 'descending'
if (that.hasOwnProperty(key)) { * @return {Function} The sort function
if (typeof that[key] === "function") { */
Object.defineProperty(that, key, { function sortFunction(key, way) {
"configurable": option.secure_methods ? false : true, if (way === 'descending') {
"enumerable": option.hide_methods ? false : true, return function (a, b) {
"writable": option.secure_methods ? false : true, // this comparison is 5 times faster than json comparison
"value": that[key] 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') {
function postClassCreation(that) { return function (a, b) {
// modify the object according to 'option' // this comparison is 5 times faster than json comparison
var key; var i, l;
if (option) { a = metadataValueToStringArray(a[key]) || [];
for (key in that) { b = metadataValueToStringArray(b[key]) || [];
if (that.hasOwnProperty(key)) { l = a.length > b.length ? a.length : b.length;
if (typeof that[key] === "function") { for (i = 0; i < l; i += 1) {
Object.defineProperty(that, key, { if (a[i] === undefined) {
"configurable": option.secure_static_methods === return -1;
false ? true : false, }
"enumerable": option.hide_static_methods ? false : true, if (b[i] === undefined) {
"writable": option.secure_static_methods === false ? true : false, return 1;
"value": that[key] }
}); if (a[i] > b[i]) {
} return 1;
}
if (a[i] < b[i]) {
return -1;
} }
} }
return 0;
};
}
throw new TypeError("complex_queries.sortFunction(): " +
"Argument 2 must be 'ascending' or 'descending'");
}
/**
* Clones all native object in deep. Managed types: Object, Array, String,
* Number, Boolean, null.
*
* @param {A} object The object to clone
* @return {A} The cloned object
*/
function deepClone(object) {
var i, cloned;
if (Array.isArray(object)) {
cloned = [];
for (i = 0; i < object.length; i += 1) {
cloned[i] = deepClone(object[i]);
}
return cloned;
}
if (typeof object === "object") {
cloned = {};
for (i in object) {
if (object.hasOwnProperty(i)) {
cloned[i] = deepClone(object[i]);
}
} }
return cloned;
} }
return object;
}
new_class = function (spec, my) { /**
var i; * Inherits the prototype methods from one constructor into another. The
spec = spec || {}; * prototype of `constructor` will be set to a new object created from
my = my || {}; * `superConstructor`.
// don't use forEach ! *
for (i = 0; i < constructors.length; i += 1) { * @param {Function} constructor The constructor which inherits the super one
constructors[i].apply(this, [spec, my]); * @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
} }
postObjectCreation(this); });
return this; }
};
option = option || {}; /**
option.static_methods = option.static_methods || {}; * Does nothing
for (j in option.static_methods) { */
if (option.static_methods.hasOwnProperty(j)) { function emptyFunction() {}
new_class[j] = option.static_methods[j];
/**
* 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("complex_queries.select(): " +
"Argument 1 is not of type Array");
}
if (!Array.isArray(list)) {
throw new TypeError("complex_queries.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) {
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;
}
} }
} }
postClassCreation(new_class); return list;
return new_class;
} }
_export('select', select);
/** /**
* Escapes regexp special chars from a string. * Sort a list of items, according to keys and directions. If `clone` is true,
* then the method will act on a cloned list.
* *
* @param {String} string The string to escape * @param {Array} sort_on_option List of couples [key, direction]
* @return {String} The escaped string * @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 stringEscapeRegexpCharacters(string) { function sortOn(sort_on_option, list, clone) {
if (typeof string === "string") { var sort_index;
return string.replace(/([\\\.\$\[\]\(\)\{\}\^\?\*\+\-])/g, "\\$1"); if (!Array.isArray(sort_on_option)) {
throw new TypeError("complex_queries.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;
} }
_export("stringEscapeRegexpCharacters", stringEscapeRegexpCharacters); _export('sortOn', sortOn);
/** /**
* A sort function to sort items by key * Limit a list of items, according to index and length. If `clone` is true,
* then the method will act on a cloned list.
* *
* @param {String} key The key to sort on * @param {Array} limit_option A couple [from, length]
* @param {String} [way="ascending"] 'ascending' or 'descending' * @param {Array} list The item list to limit
* @return {Function} The sort function * @param {Boolean} [clone=false] If true, modifies a clone of the list
* @return {Array} The filtered list
*/ */
function sortFunction(key, way) { function limit(limit_option, list, clone) {
if (way === 'descending') { if (!Array.isArray(limit_option)) {
return function (a, b) { throw new TypeError("complex_queries.limit(): " +
return a[key] < b[key] ? 1 : a[key] > b[key] ? -1 : 0; "Argument 1 is not of type 'array'");
};
} }
return function (a, b) { if (!Array.isArray(list)) {
return a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : 0; throw new TypeError("complex_queries.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;
} }
_export('limit', limit);
/**
* Convert a search text to a regexp.
*
* @param {String} string The string to convert
* @param {String} [wildcard_character=undefined] The wildcard chararter
* @return {RegExp} The search text regexp
*/
function convertStringToRegExp(string, wildcard_character) {
if (typeof string !== 'string') {
throw new TypeError("complex_queries.convertStringToRegExp(): " +
"Argument 1 is not of type 'string'");
}
if (wildcard_character === undefined ||
wildcard_character === null || wildcard_character === '') {
return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$");
}
if (typeof wildcard_character !== 'string' || wildcard_character.length > 1) {
throw new TypeError("complex_queries.convertStringToRegExp(): " +
"Optional argument 2 must be a string of length <= 1");
}
return new RegExp("^" + stringEscapeRegexpCharacters(string).replace(
new RegExp(stringEscapeRegexpCharacters(wildcard_character), 'g'),
'.*'
) + "$");
}
_export('convertStringToRegExp', convertStringToRegExp);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global _export: true, ComplexQuery: true, SimpleQuery: true, /*global _export: true, ComplexQuery: true, SimpleQuery: true, Query: true,
newClass: true, Query: true */ parseStringToObject: true */
var query_class_dict = {}, QueryFactory; var query_class_dict = {};
/** /**
* Provides static methods to create Query object * Provides static methods to create Query object
* *
* @class QueryFactory * @class QueryFactory
*/ */
QueryFactory = newClass({ function QueryFactory() {}
"static_methods": {
/**
/** * Creates Query object from a search text string or a serialized version
* Creates Query object from a search text string or a serialized version * of a Query.
* of a Query. *
* * @method create
* @method create * @static
* @static * @param {Object,String} object The search text or the serialized version
* @param {Object,String} object The search text or the serialized version * of a Query
* of a Query * @return {Query} A Query object
* @return {Query} A Query object */
*/ QueryFactory.create = function (object) {
"create": function (object) { if (object === "") {
if (object === "") { return new Query();
return new Query();
}
if (typeof object === "string") {
object = Query.parseStringToObject(object);
}
if (typeof (object || {}).type === "string" &&
query_class_dict[object.type]) {
return new query_class_dict[object.type](object);
}
return null;
}
} }
}, function () {}); 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);
}
throw new TypeError("QueryFactory.create(): " +
"Argument 1 is not a search text or a parsable object");
};
_export("QueryFactory", QueryFactory); _export("QueryFactory", QueryFactory);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global newClass: true, sortFunction: true, parseStringToObject: true, /*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:
_export: true, stringEscapeRegexpCharacters: true */ true, select: true, _export: true, stringEscapeRegexpCharacters: true,
deepClone: true */
/** /**
* The query to use to filter a list of objects. * The query to use to filter a list of objects.
...@@ -897,99 +1059,7 @@ _export("QueryFactory", QueryFactory); ...@@ -897,99 +1059,7 @@ _export("QueryFactory", QueryFactory);
* @class Query * @class Query
* @constructor * @constructor
*/ */
var Query = newClass(function () { function Query() {
var that = this, emptyFunction = function () {};
/**
* 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 {String} [option.wildcard_character="%"] The wildcard character
* @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.
*/
that.exec = function (item_list, option) {
var i = 0;
while (i < item_list.length) {
if (!that.match(item_list[i], option.wildcard_character)) {
item_list.splice(i, 1);
} else {
i += 1;
}
}
if (option.sort_on) {
Query.sortOn(option.sort_on, item_list);
}
if (option.limit) {
item_list.splice(0, option.limit[0]);
if (option.limit[1]) {
item_list.splice(option.limit[1]);
}
}
Query.filterListSelect(option.select_list || [], 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
*/
that.match = function (item, wildcard_character) {
return true;
};
/**
* The recursive parser.
*
* @method recParse
* @private
* @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 i, query = object.parsed;
if (query.type === "complex") {
for (i = 0; i < query.query_list.length; i += 1) {
object.parsed = query.query_list[i];
recParse(object, option);
query.query_list[i] = object.parsed;
}
object.parsed = query;
that.onParseComplexQuery(object, option);
} else if (query.type === "simple") {
that.onParseSimpleQuery(object, option);
}
}
/**
* 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
*/
that.parse = function (option) {
var object;
object = {"parsed": JSON.parse(JSON.stringify(that.serialized()))};
that.onParseStart(object, option);
recParse(object, option);
that.onParseEnd(object, option);
return object.parsed;
};
/** /**
* Called before parsing the query. Must be overridden! * Called before parsing the query. Must be overridden!
...@@ -998,7 +1068,7 @@ var Query = newClass(function () { ...@@ -998,7 +1068,7 @@ var Query = newClass(function () {
* @param {Object} object The object shared in the parse process * @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse() * @param {Object} option Some option gave in parse()
*/ */
that.onParseStart = emptyFunction; this.onParseStart = emptyFunction;
/** /**
* Called when parsing a simple query. Must be overridden! * Called when parsing a simple query. Must be overridden!
...@@ -1007,7 +1077,7 @@ var Query = newClass(function () { ...@@ -1007,7 +1077,7 @@ var Query = newClass(function () {
* @param {Object} object The object shared in the parse process * @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse() * @param {Object} option Some option gave in parse()
*/ */
that.onParseSimpleQuery = emptyFunction; this.onParseSimpleQuery = emptyFunction;
/** /**
* Called when parsing a complex query. Must be overridden! * Called when parsing a complex query. Must be overridden!
...@@ -1016,7 +1086,7 @@ var Query = newClass(function () { ...@@ -1016,7 +1086,7 @@ var Query = newClass(function () {
* @param {Object} object The object shared in the parse process * @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse() * @param {Object} option Some option gave in parse()
*/ */
that.onParseComplexQuery = emptyFunction; this.onParseComplexQuery = emptyFunction;
/** /**
* Called after parsing the query. Must be overridden! * Called after parsing the query. Must be overridden!
...@@ -1025,105 +1095,133 @@ var Query = newClass(function () { ...@@ -1025,105 +1095,133 @@ var Query = newClass(function () {
* @param {Object} object The object shared in the parse process * @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse() * @param {Object} option Some option gave in parse()
*/ */
that.onParseEnd = emptyFunction; this.onParseEnd = emptyFunction;
/** }
* Convert this query to a parsable string.
*
* @method toString
* @return {String} The string version of this query
*/
that.toString = function () {
return "";
};
/** /**
* Convert this query to an jsonable object in order to be remake thanks to * Filter the item list with matching item only
* QueryFactory class. *
* * @method exec
* @method serialized * @param {Array} item_list The list of object
* @return {Object} The jsonable object * @param {Object} [option] Some operation option
*/ * @param {String} [option.wildcard_character="%"] The wildcard character
that.serialized = function () { * @param {Array} [option.select_list] A object keys to retrieve
return undefined; * @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 = 0;
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'");
}
if (option.wildcard_character === undefined) {
option.wildcard_character = '%';
}
while (i < item_list.length) {
if (!this.match(item_list[i], option.wildcard_character)) {
item_list.splice(i, 1);
} else {
i += 1;
}
}
if (option.sort_on) {
sortOn(option.sort_on, item_list);
}
if (option.limit) {
limit(option.limit, item_list);
}
select(option.select_list || [], item_list);
};
}, {"static_methods": { /**
* 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 (item, wildcard_character) {
return true;
};
/**
* Filter a list of items, modifying them to select only wanted keys.
*
* @method filterListSelect
* @static
* @param {Array} select_option Key list to keep
* @param {Array} list The item list to filter
*/
"filterListSelect": function (select_option, list) {
var i, j, new_item;
for (i = 0; i < list.length; i += 1) {
new_item = {};
for (j = 0; j < select_option.length; j += 1) {
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;
}
}
}
},
/**
* 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;
/** /**
* Sort a list of items, according to keys and directions. * The recursive parser.
* *
* @method sortOn * @param {Object} object The object shared in the parse process
* @static * @param {Object} options Some options usable in the parseMethods
* @param {Array} sort_on_option List of couples [key, direction] * @return {Any} The parser result
* @param {Array} list The item list to sort
*/ */
"sortOn": function (sort_on_option, list) { function recParse(object, option) {
var sort_index; var i, query = object.parsed;
for (sort_index = sort_on_option.length - 1; sort_index >= 0; if (query.type === "complex") {
sort_index -= 1) { for (i = 0; i < query.query_list.length; i += 1) {
list.sort(sortFunction( object.parsed = query.query_list[i];
sort_on_option[sort_index][0], recParse(object, option);
sort_on_option[sort_index][1] query.query_list[i] = object.parsed;
)); }
object.parsed = query;
that.onParseComplexQuery(object, option);
} else if (query.type === "simple") {
that.onParseSimpleQuery(object, option);
} }
}, }
object = {"parsed": JSON.parse(JSON.stringify(that.serialized()))};
that.onParseStart(object, option);
recParse(object, option);
that.onParseEnd(object, option);
return object.parsed;
};
/** /**
* Parse a text request to a json query object tree * Convert this query to a parsable string.
* *
* @method parseStringToObject * @method toString
* @static * @return {String} The string version of this query
* @param {String} string The string to parse */
* @return {Object} The json query tree Query.prototype.toString = function () {
*/ return "";
"parseStringToObject": parseStringToObject, };
/** /**
* Convert a search text to a regexp. * Convert this query to an jsonable object in order to be remake thanks to
* * QueryFactory class.
* @method convertStringToRegExp *
* @static * @method serialized
* @param {String} string The string to convert * @return {Object} The jsonable object
* @param {String} [wildcard_character=undefined] The wildcard chararter */
* @return {RegExp} The search text regexp Query.prototype.serialized = function () {
*/ return undefined;
"convertStringToRegExp": function (string, wildcard_character) { };
return new RegExp("^" + stringEscapeRegexpCharacters(string).replace(
stringEscapeRegexpCharacters(wildcard_character),
'.*'
) + "$");
}
}});
_export("Query", Query); _export("Query", Query);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global newClass: true, Query: true, /*global Query: true, inherits: true, query_class_dict: true, _export: true,
query_class_dict: true, _export: true */ convertStringToRegExp: true */
/** /**
* The SimpleQuery inherits from Query, and compares one metadata value * The SimpleQuery inherits from Query, and compares one metadata value
...@@ -1135,7 +1233,9 @@ _export("Query", Query); ...@@ -1135,7 +1233,9 @@ _export("Query", Query);
* @param {String} spec.key The metadata key * @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare * @param {String} spec.value The value of the metadata to compare
*/ */
var SimpleQuery = newClass(Query, function (spec) { function SimpleQuery(spec) {
Query.call(this);
/** /**
* Operator to use to compare object values * Operator to use to compare object values
* *
...@@ -1162,124 +1262,206 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -1162,124 +1262,206 @@ var SimpleQuery = newClass(Query, function (spec) {
*/ */
this.value = spec.value; this.value = spec.value;
/** }
* #crossLink "Query/match:method" inherits(SimpleQuery, Query);
*/
this.match = function (item, wildcard_character) {
return this[this.operator](item[this.key], this.value, wildcard_character);
};
/** /**
* #crossLink "Query/toString:method" * #crossLink "Query/match:method"
*/ */
this.toString = function () { SimpleQuery.prototype.match = function (item, wildcard_character) {
return (this.key ? this.key + ": " : "") + (this.operator || "=") + ' "' + return this[this.operator](item[this.key], this.value, wildcard_character);
this.value + '"'; };
};
/** /**
* #crossLink "Query/serialized:method" * #crossLink "Query/toString:method"
*/ */
this.serialized = function () { SimpleQuery.prototype.toString = function () {
return { return (this.key ? this.key + ": " : "") + (this.operator || "=") + ' "' +
"type": "simple", this.value + '"';
"operator": this.operator, };
"key": this.key,
"value": this.value
};
};
/** /**
* Comparison operator, test if this query value matches the item value * #crossLink "Query/serialized:method"
* */
* @method = SimpleQuery.prototype.serialized = function () {
* @param {String} object_value The value to compare return {
* @param {String} comparison_value The comparison value "type": "simple",
* @param {String} wildcard_character The wildcard_character "operator": this.operator,
* @return {Boolean} true if match, false otherwise "key": this.key,
*/ "value": this.value
this["="] = function (object_value, comparison_value,
wildcard_character) {
return Query.convertStringToRegExp(
comparison_value.toString(),
wildcard_character || "%"
).test(object_value.toString());
}; };
};
/** /**
* Comparison operator, test if this query value does not match the item value * Comparison operator, test if this query value matches the item value
* *
* @method != * @method =
* @param {String} object_value The value to compare * @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value * @param {String} comparison_value The comparison value
* @param {String} wildcard_character The wildcard_character * @param {String} wildcard_character The wildcard_character
* @return {Boolean} true if not match, false otherwise * @return {Boolean} true if match, false otherwise
*/ */
this["!="] = function (object_value, comparison_value, SimpleQuery.prototype["="] = function (object_value, comparison_value,
wildcard_character) { wildcard_character) {
return !Query.convertStringTextToRegExp( var value, i;
comparison_value.toString(), if (!Array.isArray(object_value)) {
wildcard_character || "%" object_value = [object_value];
).test(object_value.toString()); }
}; for (i = 0; i < object_value.length; i += 1) {
value = object_value[i];
if (typeof value === 'object') {
value = value.content;
}
if (comparison_value === undefined) {
if (value === undefined) {
return true;
}
return false;
}
if (value === undefined) {
return false;
}
if (
convertStringToRegExp(
comparison_value.toString(),
wildcard_character
).test(value.toString())
) {
return true;
}
}
return false;
};
/** /**
* Comparison operator, test if this query value is lower than the item value * Comparison operator, test if this query value does not match the item value
* *
* @method < * @method !=
* @param {Number, String} object_value The value to compare * @param {String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value * @param {String} comparison_value The comparison value
* @return {Boolean} true if lower, false otherwise * @param {String} wildcard_character The wildcard_character
*/ * @return {Boolean} true if not match, false otherwise
this["<"] = function (object_value, comparison_value) { */
return object_value < comparison_value; SimpleQuery.prototype["!="] = function (object_value, comparison_value,
}; wildcard_character) {
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 = value.content;
}
if (comparison_value === undefined) {
if (value === undefined) {
return false;
}
return true;
}
if (value === undefined) {
return true;
}
if (
convertStringToRegExp(
comparison_value.toString(),
wildcard_character
).test(value.toString())
) {
return false;
}
}
return true;
};
/** /**
* Comparison operator, test if this query value is equal or lower than the * Comparison operator, test if this query value is lower than the item value
* item value *
* * @method <
* @method <= * @param {Number, String} object_value The value to compare
* @param {Number, String} object_value The value to compare * @param {Number, String} comparison_value The comparison value
* @param {Number, String} comparison_value The comparison value * @return {Boolean} true if lower, false otherwise
* @return {Boolean} true if equal or lower, false otherwise */
*/ SimpleQuery.prototype["<"] = function (object_value, comparison_value) {
this["<="] = function (object_value, comparison_value) { var value;
return object_value <= comparison_value; if (!Array.isArray(object_value)) {
}; object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object') {
value = value.content;
}
return value < comparison_value;
};
/** /**
* Comparison operator, test if this query value is greater than the item * Comparison operator, test if this query value is equal or lower than the
* value * item value
* *
* @method > * @method <=
* @param {Number, String} object_value The value to compare * @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value * @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if greater, false otherwise * @return {Boolean} true if equal or lower, false otherwise
*/ */
this[">"] = function (object_value, comparison_value) { SimpleQuery.prototype["<="] = function (object_value, comparison_value) {
return object_value > comparison_value; var value;
}; if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object') {
value = value.content;
}
return value <= comparison_value;
};
/** /**
* Comparison operator, test if this query value is equal or greater than the * Comparison operator, test if this query value is greater than the item
* item value * value
* *
* @method >= * @method >
* @param {Number, String} object_value The value to compare * @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value * @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or greater, false otherwise * @return {Boolean} true if greater, false otherwise
*/ */
this[">="] = function (object_value, comparison_value) { SimpleQuery.prototype[">"] = function (object_value, comparison_value) {
return object_value >= comparison_value; var value;
}; if (!Array.isArray(object_value)) {
}); object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object') {
value = value.content;
}
return 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 = value.content;
}
return value >= comparison_value;
};
query_class_dict.simple = SimpleQuery; query_class_dict.simple = SimpleQuery;
_export("SimpleQuery", SimpleQuery); _export("SimpleQuery", SimpleQuery);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global newClass: true, Query: true, query_class_dict: true, /*global Query: true, query_class_dict: true, inherits: true,
_export: true, QueryFactory: true */ _export: true, QueryFactory: true */
/** /**
...@@ -1293,7 +1475,8 @@ _export("SimpleQuery", SimpleQuery); ...@@ -1293,7 +1475,8 @@ _export("SimpleQuery", SimpleQuery);
* @param {String} spec.key The metadata key * @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare * @param {String} spec.value The value of the metadata to compare
*/ */
var ComplexQuery = newClass(Query, function (spec) { function ComplexQuery(spec) {
Query.call(this);
/** /**
* Logical operator to use to compare object values * Logical operator to use to compare object values
...@@ -1316,100 +1499,102 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -1316,100 +1499,102 @@ var ComplexQuery = newClass(Query, function (spec) {
this.query_list = spec.query_list || []; this.query_list = spec.query_list || [];
this.query_list = this.query_list.map(QueryFactory.create); this.query_list = this.query_list.map(QueryFactory.create);
/** }
* #crossLink "Query/match:method" inherits(ComplexQuery, Query);
*/
this.match = function (item, wildcard_character) {
return this[this.operator](item, wildcard_character);
};
/** /**
* #crossLink "Query/toString:method" * #crossLink "Query/match:method"
*/ */
this.toString = function () { ComplexQuery.prototype.match = function (item, wildcard_character) {
var str_list = ["("], this_operator = this.operator; return this[this.operator](item, wildcard_character);
this.query_list.forEach(function (query) { };
str_list.push(query.toString());
str_list.push(this_operator);
});
str_list.pop(); // remove last operator
str_list.push(")");
return str_list.join(" ");
};
/** /**
* #crossLink "Query/serialized:method" * #crossLink "Query/toString:method"
*/ */
this.serialized = function () { ComplexQuery.prototype.toString = function () {
var s = { var str_list = ["("], this_operator = this.operator;
"type": "complex", this.query_list.forEach(function (query) {
"operator": this.operator, str_list.push(query.toString());
"query_list": [] str_list.push(this_operator);
}; });
this.query_list.forEach(function (query) { str_list.pop(); // remove last operator
s.query_list.push(query.serialized()); str_list.push(")");
}); return str_list.join(" ");
return s; };
/**
* #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(query.serialized());
});
return s;
};
/** /**
* Comparison operator, test if all sub queries match the * Comparison operator, test if all sub queries match the
* item value * item value
* *
* @method AND * @method AND
* @param {Object} item The item to match * @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character * @param {String} wildcard_character The wildcard character
* @return {Boolean} true if all match, false otherwise * @return {Boolean} true if all match, false otherwise
*/ */
this.AND = function (item, wildcard_character) { ComplexQuery.prototype.AND = function (item, wildcard_character) {
var i; var i;
for (i = 0; i < this.query_list.length; i += 1) { for (i = 0; i < this.query_list.length; i += 1) {
if (!this.query_list[i].match(item, wildcard_character)) { if (!this.query_list[i].match(item, wildcard_character)) {
return false; return false;
}
} }
return true; }
}; return true;
};
/** /**
* Comparison operator, test if one of the sub queries matches the * Comparison operator, test if one of the sub queries matches the
* item value * item value
* *
* @method OR * @method OR
* @param {Object} item The item to match * @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character * @param {String} wildcard_character The wildcard character
* @return {Boolean} true if one match, false otherwise * @return {Boolean} true if one match, false otherwise
*/ */
this.OR = function (item, wildcard_character) { ComplexQuery.prototype.OR = function (item, wildcard_character) {
var i; var i;
for (i = 0; i < this.query_list.length; i += 1) { for (i = 0; i < this.query_list.length; i += 1) {
if (this.query_list[i].match(item, wildcard_character)) { if (this.query_list[i].match(item, wildcard_character)) {
return true; return true;
}
} }
return false; }
}; return false;
};
/** /**
* Comparison operator, test if the sub query does not match the * Comparison operator, test if the sub query does not match the
* item value * item value
* *
* @method NOT * @method NOT
* @param {Object} item The item to match * @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character * @param {String} wildcard_character The wildcard character
* @return {Boolean} true if one match, false otherwise * @return {Boolean} true if one match, false otherwise
*/ */
this.NOT = function (item, wildcard_character) { ComplexQuery.prototype.NOT = function (item, wildcard_character) {
return !this.query_list[0].match(item, wildcard_character); return !this.query_list[0].match(item, wildcard_character);
}; };
});
query_class_dict.complex = ComplexQuery; query_class_dict.complex = ComplexQuery;
_export("ComplexQuery", ComplexQuery); _export("ComplexQuery", ComplexQuery);
if (typeof define === "function" && define.amd) { if (typeof define === "function" && define.amd) {
// define(to_export); // define(to_export);
Object.defineProperty(window, module_name, { Object.defineProperty(window, module_name, {
configurable: false, configurable: false,
enumerable: true, enumerable: true,
......
...@@ -401,7 +401,7 @@ ...@@ -401,7 +401,7 @@
*/ */
function indexStorage(spec, my) { function indexStorage(spec, my) {
var that, priv = {}; var that, priv = {};
that = my.basicStorage(spec, my); that = my.basicStorage(spec, my);
priv.indices = spec.indices; priv.indices = spec.indices;
...@@ -744,7 +744,7 @@ ...@@ -744,7 +744,7 @@
if (just_check) { if (just_check) {
priv.getIndexDatabase(option, index, function (current_db) { priv.getIndexDatabase(option, index, function (current_db) {
if (db.equals(current_db)) { if (db.equals(current_db)) {
return that.success({"ok": true, "_id": command.getDocId()}); return that.success({"ok": true, "id": command.getDocId()});
} }
return that.error(generateErrorObject( return that.error(generateErrorObject(
"Different Index", "Different Index",
...@@ -754,7 +754,7 @@ ...@@ -754,7 +754,7 @@
}); });
} else { } else {
priv.storeIndexDatabaseList(db_list, {}, function () { priv.storeIndexDatabaseList(db_list, {}, function () {
that.success({"ok": true, "_id": command.getDocId()}); that.success({"ok": true, "id": command.getDocId()});
}); });
} }
}, },
......
...@@ -531,13 +531,13 @@ var command = function (spec, my) { ...@@ -531,13 +531,13 @@ var command = function (spec, my) {
* @param {object} storage The storage. * @param {object} storage The storage.
*/ */
that.validate = function (storage) { that.validate = function (storage) {
if (typeof priv.doc._id === "string" && priv.doc._id.match(" ")) { if (typeof priv.doc._id === "string" && priv.doc._id === "") {
that.error({ that.error({
"status": 21, "status": 21,
"statusText": "Invalid Document Id", "statusText": "Invalid Document Id",
"error": "invalid_document_id", "error": "invalid_document_id",
"message": "The document id is invalid", "message": "The document id is invalid",
"reason": "The document id contains spaces" "reason": "empty"
}); });
return false; return false;
} }
......
...@@ -11,6 +11,37 @@ ...@@ -11,6 +11,37 @@
/** /**
* JIO Local Storage. Type = 'local'. * JIO Local Storage. Type = 'local'.
* Local browser "database" storage. * Local browser "database" storage.
*
* Storage Description:
*
* {
* "type": "local",
* "username": <non empty string>, // to define user space
* "application_name": <string> // default 'untitled'
* }
*
* Document are stored in path
* 'jio/localstorage/username/application_name/document_id' like this:
*
* {
* "_id": "document_id",
* "_attachments": {
* "attachment_name": {
* "length": data_length,
* "digest": "md5-XXX",
* "content_type": "mime/type"
* },
* "attachment_name2": {..}, ...
* },
* "metadata_name": "metadata_value"
* "metadata_name2": ...
* ...
* }
*
* Only "_id" and "_attachments" are specific metadata keys, other one can be
* added without loss.
*
* @class LocalStorage
*/ */
jIO.addStorageType('local', function (spec, my) { jIO.addStorageType('local', function (spec, my) {
...@@ -64,34 +95,6 @@ jIO.addStorageType('local', function (spec, my) { ...@@ -64,34 +95,6 @@ jIO.addStorageType('local', function (spec, my) {
S4() + S4(); S4() + S4();
}; };
/**
* Update [doc] the document object and remove [doc] keys
* which are not in [new_doc]. It only changes [doc] keys not starting
* with an underscore.
* ex: doc: {key:value1,_key:value2} with
* new_doc: {key:value3,_key:value4} updates
* doc: {key:value3,_key:value2}.
* @param {object} doc The original document object.
* @param {object} new_doc The new document object
*/
priv.documentObjectUpdate = function (doc, new_doc) {
var k;
for (k in doc) {
if (doc.hasOwnProperty(k)) {
if (k[0] !== '_') {
delete doc[k];
}
}
}
for (k in new_doc) {
if (new_doc.hasOwnProperty(k)) {
if (k[0] !== '_') {
doc[k] = new_doc[k];
}
}
}
};
/** /**
* Checks if an object has no enumerable keys * Checks if an object has no enumerable keys
* @method objectIsEmpty * @method objectIsEmpty
...@@ -137,11 +140,11 @@ jIO.addStorageType('local', function (spec, my) { ...@@ -137,11 +140,11 @@ jIO.addStorageType('local', function (spec, my) {
} }
doc = localstorage.getItem(priv.localpath + "/" + doc_id); doc = localstorage.getItem(priv.localpath + "/" + doc_id);
if (doc === null) { if (doc === null) {
// the document does not exist
doc = command.cloneDoc(); doc = command.cloneDoc();
doc._id = doc_id; doc._id = doc_id;
// the document does not exist delete doc._attachments;
localstorage.setItem(priv.localpath + "/" + doc_id, localstorage.setItem(priv.localpath + "/" + doc_id, doc);
doc);
that.success({ that.success({
"ok": true, "ok": true,
"id": doc_id "id": doc_id
...@@ -166,14 +169,17 @@ jIO.addStorageType('local', function (spec, my) { ...@@ -166,14 +169,17 @@ jIO.addStorageType('local', function (spec, my) {
*/ */
that.put = function (command) { that.put = function (command) {
setTimeout(function () { setTimeout(function () {
var doc; var doc, tmp;
doc = localstorage.getItem(priv.localpath + "/" + command.getDocId()); doc = localstorage.getItem(priv.localpath + "/" + command.getDocId());
if (doc === null) { if (doc === null) {
// the document does not exist // the document does not exist
doc = command.cloneDoc(); doc = command.cloneDoc();
delete doc._attachments;
} else { } else {
// the document already exists // the document already exists
priv.documentObjectUpdate(doc, command.cloneDoc()); tmp = command.cloneDoc();
tmp._attachments = doc._attachments;
doc = tmp;
} }
// write // write
localstorage.setItem(priv.localpath + "/" + command.getDocId(), doc); localstorage.setItem(priv.localpath + "/" + command.getDocId(), doc);
......
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