Commit a8eb67be authored by Tristan Cavelier's avatar Tristan Cavelier

complex queries module redesigned to moved methods

parent 7cc44e36
This diff is collapsed.
/*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 */
/** /**
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
* @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
...@@ -36,17 +37,21 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -36,17 +37,21 @@ 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);
/** }
inherits(ComplexQuery, Query);
ComplexQuery.prototype.constructor = ComplexQuery;
/**
* #crossLink "Query/match:method" * #crossLink "Query/match:method"
*/ */
this.match = function (item, wildcard_character) { ComplexQuery.prototype.match = function (item, wildcard_character) {
return this[this.operator](item, wildcard_character); return this[this.operator](item, wildcard_character);
}; };
/** /**
* #crossLink "Query/toString:method" * #crossLink "Query/toString:method"
*/ */
this.toString = function () { ComplexQuery.prototype.toString = function () {
var str_list = ["("], this_operator = this.operator; var str_list = ["("], this_operator = this.operator;
this.query_list.forEach(function (query) { this.query_list.forEach(function (query) {
str_list.push(query.toString()); str_list.push(query.toString());
...@@ -55,12 +60,12 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -55,12 +60,12 @@ var ComplexQuery = newClass(Query, function (spec) {
str_list.pop(); // remove last operator str_list.pop(); // remove last operator
str_list.push(")"); str_list.push(")");
return str_list.join(" "); return str_list.join(" ");
}; };
/** /**
* #crossLink "Query/serialized:method" * #crossLink "Query/serialized:method"
*/ */
this.serialized = function () { ComplexQuery.prototype.serialized = function () {
var s = { var s = {
"type": "complex", "type": "complex",
"operator": this.operator, "operator": this.operator,
...@@ -70,9 +75,9 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -70,9 +75,9 @@ var ComplexQuery = newClass(Query, function (spec) {
s.query_list.push(query.serialized()); s.query_list.push(query.serialized());
}); });
return s; return s;
}; };
/** /**
* Comparison operator, test if all sub queries match the * Comparison operator, test if all sub queries match the
* item value * item value
* *
...@@ -81,7 +86,7 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -81,7 +86,7 @@ var ComplexQuery = newClass(Query, function (spec) {
* @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)) {
...@@ -89,9 +94,9 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -89,9 +94,9 @@ var ComplexQuery = newClass(Query, function (spec) {
} }
} }
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
* *
...@@ -100,7 +105,7 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -100,7 +105,7 @@ var ComplexQuery = newClass(Query, function (spec) {
* @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)) {
...@@ -108,9 +113,9 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -108,9 +113,9 @@ var ComplexQuery = newClass(Query, function (spec) {
} }
} }
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
* *
...@@ -119,10 +124,9 @@ var ComplexQuery = newClass(Query, function (spec) { ...@@ -119,10 +124,9 @@ var ComplexQuery = newClass(Query, function (spec) {
* @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;
......
/**
* 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) {
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 newClass: true, sortFunction: true, parseStringToObject: true, /*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:
_export: true, stringEscapeRegexpCharacters: true, deepClone: 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.
...@@ -9,11 +10,48 @@ ...@@ -9,11 +10,48 @@
* @class Query * @class Query
* @constructor * @constructor
*/ */
var Query = newClass(function () { function Query() {
var that = this, emptyFunction = function () {}; /**
* 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;
}
Query.prototype.constructor = Query;
/**
* Filter the item list with matching item only * Filter the item list with matching item only
* *
* @method exec * @method exec
...@@ -26,40 +64,53 @@ var Query = newClass(function () { ...@@ -26,40 +64,53 @@ var Query = newClass(function () {
* @param {Array} [option.limit] Couple of integer, first is an index and * @param {Array} [option.limit] Couple of integer, first is an index and
* second is the length. * second is the length.
*/ */
that.exec = function (item_list, option) { Query.prototype.exec = function (item_list, option) {
var i = 0; var i = 0;
while (i < item_list.length) { while (i < item_list.length) {
if (!that.match(item_list[i], option.wildcard_character)) { if (!this.match(item_list[i], option.wildcard_character)) {
item_list.splice(i, 1); item_list.splice(i, 1);
} else { } else {
i += 1; i += 1;
} }
} }
if (option.sort_on) { if (option.sort_on) {
Query.sortOn(option.sort_on, item_list); sortOn(option.sort_on, item_list);
} }
if (option.limit) { if (option.limit) {
Query.limit(option.limit, item_list); limit(option.limit, item_list);
} }
Query.select(option.select_list || [], item_list); select(option.select_list || [], item_list);
}; };
/** /**
* Test if an item matches this query * Test if an item matches this query
* *
* @method match * @method match
* @param {Object} item The object to test * @param {Object} item The object to test
* @return {Boolean} true if match, false otherwise * @return {Boolean} true if match, false otherwise
*/ */
that.match = function (item, wildcard_character) { Query.prototype.match = function (item, wildcard_character) {
return true; return 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. * The recursive parser.
* *
* @method recParse
* @private
* @param {Object} object The object shared in the parse process * @param {Object} object The object shared in the parse process
* @param {Object} options Some options usable in the parseMethods * @param {Object} options Some options usable in the parseMethods
* @return {Any} The parser result * @return {Any} The parser result
...@@ -78,191 +129,32 @@ var Query = newClass(function () { ...@@ -78,191 +129,32 @@ var Query = newClass(function () {
that.onParseSimpleQuery(object, option); 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()))}; object = {"parsed": JSON.parse(JSON.stringify(that.serialized()))};
that.onParseStart(object, option); that.onParseStart(object, option);
recParse(object, option); recParse(object, option);
that.onParseEnd(object, option); that.onParseEnd(object, option);
return object.parsed; return object.parsed;
}; };
/** /**
* 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()
*/
that.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()
*/
that.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()
*/
that.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()
*/
that.onParseEnd = emptyFunction;
/**
* Convert this query to a parsable string. * Convert this query to a parsable string.
* *
* @method toString * @method toString
* @return {String} The string version of this query * @return {String} The string version of this query
*/ */
that.toString = function () { Query.prototype.toString = function () {
return ""; return "";
}; };
/** /**
* Convert this query to an jsonable object in order to be remake thanks to * Convert this query to an jsonable object in order to be remake thanks to
* QueryFactory class. * QueryFactory class.
* *
* @method serialized * @method serialized
* @return {Object} The jsonable object * @return {Object} The jsonable object
*/ */
that.serialized = function () { Query.prototype.serialized = function () {
return undefined; return undefined;
}; };
}, {"static_methods": {
/**
* 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.
*
* @method select
* @static
* @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
*/
"select": function (select_option, list, clone) {
var i, j, new_item;
if (clone) {
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;
}
}
}
return list;
},
/**
* Sort a list of items, according to keys and directions. If `clone` is true,
* then the method will act on a cloned list.
*
* @method sortOn
* @static
* @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
*/
"sortOn": function (sort_on_option, list, clone) {
var sort_index;
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;
},
/**
* Limit a list of items, according to index and length. If `clone` is true,
* then the method will act on a cloned list.
*
* @method limit
* @static
* @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
*/
"limit": function (limit_option, list, clone) {
if (clone) {
list = deepClone(list);
}
list.splice(0, limit_option[0]);
if (limit_option[1]) {
list.splice(limit_option[1]);
}
return list;
},
/**
* Parse a text request to a json query object tree
*
* @method parseStringToObject
* @static
* @param {String} string The string to parse
* @return {Object} The json query tree
*/
"parseStringToObject": parseStringToObject,
/**
* Convert a search text to a regexp.
*
* @method convertStringToRegExp
* @static
* @param {String} string The string to convert
* @param {String} [wildcard_character=undefined] The wildcard chararter
* @return {RegExp} The search text regexp
*/
"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 _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.
* *
...@@ -23,20 +22,18 @@ QueryFactory = newClass({ ...@@ -23,20 +22,18 @@ QueryFactory = newClass({
* of a Query * of a Query
* @return {Query} A Query object * @return {Query} A Query object
*/ */
"create": function (object) { QueryFactory.create = function (object) {
if (object === "") { if (object === "") {
return new Query(); return new Query();
} }
if (typeof object === "string") { if (typeof object === "string") {
object = Query.parseStringToObject(object); object = parseStringToObject(object);
} }
if (typeof (object || {}).type === "string" && if (typeof (object || {}).type === "string" &&
query_class_dict[object.type]) { query_class_dict[object.type]) {
return new query_class_dict[object.type](object); return new query_class_dict[object.type](object);
} }
return null; return null;
} };
}
}, function () {});
_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, 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
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
* @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
* *
...@@ -39,34 +41,38 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -39,34 +41,38 @@ var SimpleQuery = newClass(Query, function (spec) {
*/ */
this.value = spec.value; this.value = spec.value;
/** }
inherits(SimpleQuery, Query);
SimpleQuery.prototype.constructor = SimpleQuery;
/**
* #crossLink "Query/match:method" * #crossLink "Query/match:method"
*/ */
this.match = function (item, wildcard_character) { SimpleQuery.prototype.match = function (item, wildcard_character) {
return this[this.operator](item[this.key], this.value, wildcard_character); return this[this.operator](item[this.key], this.value, wildcard_character);
}; };
/** /**
* #crossLink "Query/toString:method" * #crossLink "Query/toString:method"
*/ */
this.toString = function () { SimpleQuery.prototype.toString = function () {
return (this.key ? this.key + ": " : "") + (this.operator || "=") + ' "' + return (this.key ? this.key + ": " : "") + (this.operator || "=") + ' "' +
this.value + '"'; this.value + '"';
}; };
/** /**
* #crossLink "Query/serialized:method" * #crossLink "Query/serialized:method"
*/ */
this.serialized = function () { SimpleQuery.prototype.serialized = function () {
return { return {
"type": "simple", "type": "simple",
"operator": this.operator, "operator": this.operator,
"key": this.key, "key": this.key,
"value": this.value "value": this.value
}; };
}; };
/** /**
* Comparison operator, test if this query value matches the item value * Comparison operator, test if this query value matches the item value
* *
* @method = * @method =
...@@ -75,15 +81,15 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -75,15 +81,15 @@ var SimpleQuery = newClass(Query, function (spec) {
* @param {String} wildcard_character The wildcard_character * @param {String} wildcard_character The wildcard_character
* @return {Boolean} true if 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.convertStringToRegExp( return convertStringToRegExp(
comparison_value.toString(), comparison_value.toString(),
wildcard_character || "%" wildcard_character || "%"
).test(object_value.toString()); ).test(object_value.toString());
}; };
/** /**
* Comparison operator, test if this query value does not match the item value * Comparison operator, test if this query value does not match the item value
* *
* @method != * @method !=
...@@ -92,15 +98,15 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -92,15 +98,15 @@ var SimpleQuery = newClass(Query, function (spec) {
* @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 not match, false otherwise
*/ */
this["!="] = function (object_value, comparison_value, SimpleQuery.prototype["!="] = function (object_value, comparison_value,
wildcard_character) { wildcard_character) {
return !Query.convertStringTextToRegExp( return !convertStringToRegExp(
comparison_value.toString(), comparison_value.toString(),
wildcard_character || "%" wildcard_character || "%"
).test(object_value.toString()); ).test(object_value.toString());
}; };
/** /**
* Comparison operator, test if this query value is lower than the item value * Comparison operator, test if this query value is lower than the item value
* *
* @method < * @method <
...@@ -108,11 +114,11 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -108,11 +114,11 @@ var SimpleQuery = newClass(Query, function (spec) {
* @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 lower, false otherwise
*/ */
this["<"] = function (object_value, comparison_value) { SimpleQuery.prototype["<"] = function (object_value, comparison_value) {
return object_value < comparison_value; return object_value < comparison_value;
}; };
/** /**
* Comparison operator, test if this query value is equal or lower than the * Comparison operator, test if this query value is equal or lower than the
* item value * item value
* *
...@@ -121,11 +127,11 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -121,11 +127,11 @@ var SimpleQuery = newClass(Query, function (spec) {
* @param {Number, String} comparison_value The comparison value * @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or lower, 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; return object_value <= comparison_value;
}; };
/** /**
* Comparison operator, test if this query value is greater than the item * Comparison operator, test if this query value is greater than the item
* value * value
* *
...@@ -134,11 +140,11 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -134,11 +140,11 @@ var SimpleQuery = newClass(Query, function (spec) {
* @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 greater, false otherwise
*/ */
this[">"] = function (object_value, comparison_value) { SimpleQuery.prototype[">"] = function (object_value, comparison_value) {
return object_value > comparison_value; return object_value > comparison_value;
}; };
/** /**
* Comparison operator, test if this query value is equal or greater than the * Comparison operator, test if this query value is equal or greater than the
* item value * item value
* *
...@@ -147,10 +153,9 @@ var SimpleQuery = newClass(Query, function (spec) { ...@@ -147,10 +153,9 @@ var SimpleQuery = newClass(Query, function (spec) {
* @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 equal or greater, false otherwise
*/ */
this[">="] = function (object_value, comparison_value) { SimpleQuery.prototype[">="] = function (object_value, comparison_value) {
return object_value >= comparison_value; return object_value >= comparison_value;
}; };
});
query_class_dict.simple = SimpleQuery; query_class_dict.simple = SimpleQuery;
......
/*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,
* protected attributes and can hide methods or/and secure methods
*
* @param {Class} Class Classes to inherit from (0..n). The last class
* parameter will inherit from the previous one, and so on
* @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() {
var j, k, constructors = [], option, new_class;
for (j = 0; j < arguments.length; j += 1) {
if (typeof arguments[j] === "function") {
constructors.push(arguments[j]);
} else if (typeof arguments[j] === "object") {
option = option || {};
for (k in arguments[j]) {
if (arguments[j].hasOwnProperty(k)) {
option[k] = arguments[j][k];
}
}
}
}
function postObjectCreation(that) {
// modify the object according to 'option'
var key;
if (option) {
for (key in that) {
if (that.hasOwnProperty(key)) {
if (typeof that[key] === "function") {
Object.defineProperty(that, key, {
"configurable": option.secure_methods ? false : true,
"enumerable": option.hide_methods ? false : true,
"writable": option.secure_methods ? false : true,
"value": that[key]
});
}
}
}
}
}
function postClassCreation(that) {
// modify the object according to 'option'
var key;
if (option) {
for (key in that) {
if (that.hasOwnProperty(key)) {
if (typeof that[key] === "function") {
Object.defineProperty(that, key, {
"configurable": option.secure_static_methods ===
false ? true : false,
"enumerable": option.hide_static_methods ? false : true,
"writable": option.secure_static_methods === false ? true : false,
"value": that[key]
});
}
}
}
}
}
new_class = function (spec, my) {
var i;
spec = spec || {};
my = my || {};
// don't use forEach !
for (i = 0; i < constructors.length; i += 1) {
constructors[i].apply(this, [spec, my]);
}
postObjectCreation(this);
return this;
};
option = option || {};
option.static_methods = option.static_methods || {};
for (j in option.static_methods) {
if (option.static_methods.hasOwnProperty(j)) {
new_class[j] = option.static_methods[j];
}
}
postClassCreation(new_class);
return new_class;
}
/** /**
* Escapes regexp special chars from a string. * Escapes regexp special chars from a string.
* *
...@@ -156,3 +60,113 @@ function deepClone(object) { ...@@ -156,3 +60,113 @@ function deepClone(object) {
} }
return object; return object;
} }
/**
* Inherits the prototype methods from one constructor into another. The
* prototype of `constructor` will be set to a new object created from
* `superConstructor`.
*/
function inherits(constructor, superConstructor) {
constructor.super_ = superConstructor;
constructor.prototype = Object.create(superConstructor.prototype, {});
}
/**
* Does nothing
*/
function emptyFunction() {}
/**
* 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 (clone) {
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;
}
}
}
return list;
}
_export('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 (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('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 (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) {
return new RegExp("^" + stringEscapeRegexpCharacters(string).replace(
stringEscapeRegexpCharacters(wildcard_character),
'.*'
) + "$");
}
_export('convertStringToRegExp', convertStringToRegExp);
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