forked from 0x2620/pandora
286 lines
12 KiB
JavaScript
286 lines
12 KiB
JavaScript
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
|
pandora.Query = (function() {
|
|
|
|
function parseFind2(str) {
|
|
// takes a find query string, returns useful information about the application's state
|
|
// (selected lists, find input key/value, group queries and selection, query object)
|
|
Ox.print('parseFind2', str)
|
|
str = str || '';
|
|
var conditions,
|
|
ret = {
|
|
find: {key: '', value: ''},
|
|
groups: [],
|
|
lists: [],
|
|
query: {conditions: [], operator: ''}
|
|
},
|
|
subconditions = str.match(/\[.*?\]/g) || [];
|
|
// replace subconditions with placeholder,
|
|
// so we can later split by main operator
|
|
subconditions.forEach(function(subcondition, i) {
|
|
subconditions[i] = subcondition.substr(1, subcondition.length - 2);
|
|
str = str.replace(subconditions[i], i);
|
|
});
|
|
if (str.indexOf(',') > -1) {
|
|
ret.query.operator = '&';
|
|
} else if (str.indexOf('|') > -1) {
|
|
ret.query.operator = '|';
|
|
}
|
|
ret.query.conditions = (
|
|
ret.query.operator == '' ? [str] : str.split(ret.query.operator == '&' ? ',' : '|')
|
|
).map(function(condition, i) {
|
|
var kv, ret;
|
|
if (condition[0] == '[') {
|
|
// re-insert subcondition
|
|
ret = parseFind2(subconditions[parseInt(condition.substr(1, condition.length - 2))]).query;
|
|
} else {
|
|
kv = ((condition.indexOf(':') > -1 ? '' : ':') + condition).split(':');
|
|
ret = Ox.extend({key: kv[0]}, parseValue(kv[1]));
|
|
}
|
|
return ret;
|
|
});
|
|
// lists are selected if exactly one condition in an & query
|
|
// or every condition in an | query
|
|
// has "list" as key and "" as operator
|
|
ret.lists = ret.query.operator == '|'
|
|
? everyCondition(ret.query.conditions, 'list', '')
|
|
: oneCondition(ret.query.conditions, 'list', '');
|
|
// find is populated if exactly one condition in an & query
|
|
// has a findKey as key and "" as operator
|
|
if (ret.query.operator == '|') {
|
|
ret.find = {key: 'advanced', value: ''};
|
|
} else {
|
|
conditions = Ox.map(pandora.site.findKeys, function(findKey) {
|
|
var values = oneCondition(ret.query.conditions, findKey.id, '');
|
|
return values.length ? {key: findKey.id, values: values} : null;
|
|
});
|
|
ret.find = conditions.length == 0 ? {key: '', value: ''}
|
|
: conditions.length == 1 && conditions[0].values.length == 1
|
|
? {key: conditions[0].key, value: conditions[0].values[0]}
|
|
: {key: 'advanced', value: ''}
|
|
}
|
|
// a group is selected if exactly one condition in an & query
|
|
// or every condition in an | query
|
|
// has the group id as key and "=" as operator
|
|
ret.groups = pandora.user.ui.groups.map(function(key) {
|
|
var selected = ret.query.operator == '|'
|
|
? everyCondition(ret.query.conditions, key, '=')
|
|
: oneCondition(ret.query.conditions, key, '='),
|
|
query = ret.query;
|
|
if (selected.length) {
|
|
query = {
|
|
conditions: Ox.map(ret.query.conditions, function(condition) {
|
|
var ret;
|
|
if (condition.conditions) {
|
|
ret = condition.conditions[0].key != key ? condition : null; // fixme: correct? see below...
|
|
} else {
|
|
ret = condition.key != key ? condition : null;
|
|
}
|
|
return ret;
|
|
}),
|
|
operator: ''
|
|
};
|
|
if (query.conditions.length == 1) {
|
|
if (query.conditions[0].conditions) {
|
|
// unwrap single remaining bracketed query
|
|
query = {
|
|
conditions: query.conditions[0].conditions,
|
|
operator: query.conditions[0].operator
|
|
}
|
|
} else {
|
|
query.operator = '';
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
query: query,
|
|
selected: selected
|
|
};
|
|
});
|
|
function oneCondition(conditions, key, operator) {
|
|
// if exactly one condition has the given key and operator
|
|
// (including conditions where all subconditions match)
|
|
// returns the corresponding value(s), otherwise returns []
|
|
var values = Ox.map(conditions, function(condition) {
|
|
var ret, same;
|
|
if (condition.conditions) {
|
|
var every = everyCondition(condition.conditions, key, operator); // fixme: what if [key|key],[key|other]?
|
|
ret = every.length ? every : null;
|
|
} else {
|
|
ret = condition.key == key && condition.operator == operator ? [condition.value] : null
|
|
}
|
|
return ret;
|
|
});
|
|
return values.length == 1 ? values[0] : [];
|
|
}
|
|
function everyCondition(conditions, key, operator) {
|
|
// if every condition has the given key and operator
|
|
// (excluding conditions where all subconditions match)
|
|
// returns the corresponding value(s), otherwise returns []
|
|
var values = Ox.map(conditions, function(condition) {
|
|
return condition.key == key && condition.operator == operator ? condition.value : null
|
|
});
|
|
return values.length == conditions.length ? values : [];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function constructFind(query) {
|
|
//Ox.print('cF', query)
|
|
return /*encodeURI(*/$.map(query.conditions, function(v, i) {
|
|
if (!Ox.isUndefined(v.conditions)) {
|
|
return '[' + constructFind(v) + ']';
|
|
} else {
|
|
return v.value !== '' ? v.key + (v.key ? ':' : '') + constructValue(v.value, v.operator) : null;
|
|
}
|
|
}).join(query.operator)/*)*/;
|
|
}
|
|
|
|
function constructValue(value, operator) {
|
|
operator = operator.replace('=', '^$');
|
|
if (operator.indexOf('$') > -1) {
|
|
value = operator.substr(0, operator.length - 1) + value + '$';
|
|
} else {
|
|
value = operator + value;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function mergeFind() {
|
|
}
|
|
|
|
function parseFind(str) {
|
|
str = str || '';
|
|
var find = {
|
|
conditions: [],
|
|
operator: ''
|
|
},
|
|
subconditions = str.match(/\[.*?\]/g) || [];
|
|
$.each(subconditions, function(i, v) {
|
|
subconditions[i] = v.substr(1, v.length - 2);
|
|
str = str.replace(v, '[' + i + ']');
|
|
});
|
|
if (str.indexOf(',') > -1) {
|
|
find.operator = '&';
|
|
} else if (str.indexOf('|') > -1) {
|
|
find.operator = '|';
|
|
}
|
|
//Ox.print('pF', str, find.operator)
|
|
find.conditions = $.map(find.operator === '' ? [str] : str.split(find.operator == '&' ? ',' : '|'), function(v, i) {
|
|
//Ox.print('v', v)
|
|
var ret, kv;
|
|
if (v[0] == '[') {
|
|
//Ox.print('recursion', subconditions)
|
|
ret = parseFind(subconditions[parseInt(v.substr(1, v.length - 2))]);
|
|
} else {
|
|
kv = ((v.indexOf(':') > - 1 ? '' : ':') + v).split(':');
|
|
if (kv[0] == 'list') { // fixme: this is just a hack
|
|
pandora.user.ui.listQuery = {conditions: [$.extend({
|
|
key: kv[0]
|
|
}, parseValue(kv[1]))], operator: ''};
|
|
} else {
|
|
ret = $.extend({
|
|
key: kv[0]
|
|
}, parseValue(kv[1]));
|
|
}
|
|
}
|
|
return ret;
|
|
});
|
|
return find;
|
|
}
|
|
|
|
function parseValue(str) {
|
|
var value = {
|
|
value: decodeURI(str),
|
|
operator: ''
|
|
};
|
|
if (value.value[0] == '!') {
|
|
value.operator = '!';
|
|
value.value = value.value.substr(1);
|
|
}
|
|
if ('^<>'.indexOf(value.value[0]) > -1) {
|
|
value.operator += value.value[0];
|
|
value.value = value.value.substr(1);
|
|
}
|
|
if (value.value.substr(-1) == '$') {
|
|
value.operator += '$';
|
|
value.value = value.value.substr(0, value.value.length - 1);
|
|
}
|
|
value.operator = value.operator.replace('^$', '=');
|
|
return value;
|
|
}
|
|
|
|
return {
|
|
|
|
fromString: function(str) {
|
|
var list = '',
|
|
query = Ox.unserialize(str.substr(1)),
|
|
sort = [];
|
|
if ('find' in query) {
|
|
Ox.print(Ox.repeat('-', 80));
|
|
Ox.print('parseFind2', parseFind2(query.find));
|
|
Ox.print(Ox.repeat('-', 80));
|
|
pandora.user.ui.listQuery = {conditions: [], operator: ''}; // fixme: hackish
|
|
pandora.user.ui.findQuery = parseFind(query.find);
|
|
if (pandora.user.ui.listQuery.conditions.length) {
|
|
list = pandora.user.ui.listQuery.conditions[0].value;
|
|
!pandora.user.ui.lists[list] && pandora.UI.set(
|
|
['lists', list].join('|'), pandora.site.user.ui.lists['']
|
|
);
|
|
}
|
|
pandora.UI.set({list: list});
|
|
//Ox.print('user.ui.findQuery', pandora.user.ui.findQuery)
|
|
}
|
|
if ('sort' in query) {
|
|
sort = query.sort.split(',');
|
|
pandora.UI.set(['lists', pandora.user.ui.list, 'sort'].join('|'), query.sort.split(',').map(function(v) {
|
|
var hasOperator = '+-'.indexOf(v[0]) > -1,
|
|
key = hasOperator ? v.substr(1) : v,
|
|
operator = hasOperator ? v[0]/*.replace('+', '')*/ : pandora.getSortOperator(key);
|
|
return {
|
|
key: key,
|
|
operator: operator
|
|
};
|
|
}));
|
|
}
|
|
if ('view' in query) {
|
|
pandora.UI.set(['lists', pandora.user.ui.list, 'listView'].join('|'), query.view);
|
|
}
|
|
},
|
|
|
|
toObject: function(groupId) {
|
|
//Ox.print('tO', pandora.user.ui.findQuery.conditions)
|
|
// the inner $.merge() creates a clone
|
|
var conditions = $.merge(
|
|
$.merge([], pandora.user.ui.listQuery.conditions),
|
|
pandora.user.ui.findQuery.conditions
|
|
),
|
|
operator;
|
|
$.merge(conditions, pandora.user.queryGroups ? $.map(pandora.user.queryGroups, function(v, i) {
|
|
if (v.id != groupId && v.query.conditions.length) {
|
|
return v.query.conditions.length == 1 ?
|
|
v.query.conditions : v.query;
|
|
}
|
|
}) : []);
|
|
operator = conditions.length < 2 ? '' : ','; // fixme: should be &
|
|
//Ox.print('>>', groupId, pandora.user.ui.find, conditions);
|
|
return {
|
|
conditions: conditions,
|
|
operator: operator
|
|
};
|
|
},
|
|
|
|
toString: function() {
|
|
//Ox.print('tS', pandora.user.ui.find)
|
|
var sort = pandora.user.ui.lists[pandora.user.ui.list].sort[0],
|
|
key = sort.key,
|
|
operator = sort.operator;
|
|
return '?' + Ox.serialize({
|
|
find: constructFind(pandora.Query.toObject()),
|
|
sort: (operator == pandora.getSortOperator(key) ? '' : operator) + key,
|
|
view: pandora.user.ui.lists[pandora.user.ui.list].listView
|
|
});
|
|
}
|
|
|
|
};
|
|
|
|
})();
|