forked from dojo/dojox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAndOrReadStore.js
executable file
·260 lines (249 loc) · 10.4 KB
/
AndOrReadStore.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
define(["dojo/_base/declare", "dojo/_base/lang", "dojo/data/ItemFileReadStore", "dojo/data/util/filter", "dojo/_base/array", "dojo/_base/json"],
function(declare, lang, ItemFileReadStore, filterUtil, array, json) {
// module:
// dojox/data/AndOrReadStore
// summary:
// TODOC
return declare("dojox.data.AndOrReadStore", [ItemFileReadStore], {
// summary:
// AndOrReadStore uses ItemFileReadStore as a base, modifying only the query (_fetchItems) section.
// Supports queries of the form: query:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"
// Includes legacy/widget support via:
// | query:{complexQuery:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"}
// The ItemFileReadStore implements the dojo/data/api/Read API and reads
// data from JSON files that have contents in this format --
// | { items: [
// | { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
// | { name:'Fozzie Bear', wears:['hat', 'tie']},
// | { name:'Miss Piggy', pets:'Foo-Foo'}
// | ]}
// Note that it can also contain an 'identifier' property that specified which attribute on the items
// in the array of items that acts as the unique identifier for that item.
_containsValue: function(/*dojo/data/api/Item*/ item, /*attribute-name-string */ attribute, /*anything*/ value,
/*String|RegExp?*/ regexp){
// summary:
// Internal function for looking at the values contained by the item.
// description:
// Internal function for looking at the values contained by the item. This
// function allows for denoting if the comparison should be case sensitive for
// strings or not (for handling filtering cases where string case should not matter)
// item:
// The data item to examine for attribute values.
// attribute:
// The attribute to inspect.
// value:
// The value to match.
// regexp:
// Optional string or regular expression generated off value if value was of string type to handle wildcarding.
// If present and attribute values are string, then it can be used for comparison instead of 'value'
// If RegExp is a string, it is treated as an comparison statement and eval for number comparisons
return array.some(this.getValues(item, attribute), function(possibleValue){
// if string... eval for math operator comparisons
if(lang.isString(regexp)){
return eval(regexp);
}else if(possibleValue !== null && !lang.isObject(possibleValue) && regexp){
if(possibleValue.toString().match(regexp)){
return true; // Boolean
}
} else if(value === possibleValue){
return true; // Boolean
} else {
return false;
}
});
},
filter: function(requestArgs, arrayOfItems, findCallback){
var items = [];
if(requestArgs.query){
//Complete copy, we may have to mess with it.
//Safer than clone, which does a shallow copy, I believe.
var query = json.fromJson(json.toJson(requestArgs.query));
//Okay, object form query, we have to check to see if someone mixed query methods (such as using FilteringSelect
//with a complexQuery). In that case, the params need to be anded to the complex query statement.
//See defect #7980
if(typeof query == "object" ){
var count = 0;
var p;
for(p in query){
count++;
}
if(count > 1 && query.complexQuery){
var cq = query.complexQuery;
var wrapped = false;
for(p in query){
if(p !== "complexQuery"){
//We should wrap this in () as it should and with the entire complex query
//Not just part of it.
if(!wrapped){
cq = "( " + cq + " )";
wrapped = true;
}
//Make sure strings are quoted when going into complexQuery merge.
var v = requestArgs.query[p];
if(lang.isString(v)){
v = "'" + v + "'";
}
cq += " AND " + p + ":" + v;
delete query[p];
}
}
query.complexQuery = cq;
}
}
var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
//for complex queries only: pattern = query[:|=]"NOT id:23* AND (type:'test*' OR dept:'bob') && !filed:true"
//logical operators are case insensitive: , NOT AND OR ( ) ! && || // "," included for quoted/string legacy queries.
if(typeof query != "string"){
query = json.toJson(query);
query = query.replace(/\\\\/g,"\\"); //counter toJson expansion of backslashes, e.g., foo\\*bar test.
}
query = query.replace(/\\"/g,"\""); //ditto, for embedded \" in lieu of " availability.
var complexQuery = lang.trim(query.replace(/{|}/g,"")); //we can handle these, too.
var pos2, i;
if(complexQuery.match(/"? *complexQuery *"?:/)){ //case where widget required a json object, so use complexQuery:'the real query'
complexQuery = lang.trim(complexQuery.replace(/"?\s*complexQuery\s*"?:/,""));
var quotes = ["'",'"'];
var pos1,colon;
var flag = false;
for(i = 0; i<quotes.length; i++){
pos1 = complexQuery.indexOf(quotes[i]);
pos2 = complexQuery.indexOf(quotes[i],1);
colon = complexQuery.indexOf(":",1);
if(pos1 === 0 && pos2 != -1 && colon < pos2){
flag = true;
break;
} //first two sets of quotes don't occur before the first colon.
}
if(flag){ //dojo.toJson, and maybe user, adds surrounding quotes, which we need to remove.
complexQuery = complexQuery.replace(/^\"|^\'|\"$|\'$/g,"");
}
} //end query="{complexQuery:'id:1* || dept:Sales'}" parsing (for when widget required json object query).
var complexQuerySave = complexQuery;
//valid logical operators.
var begRegExp = /^>=|^<=|^<|^>|^,|^NOT |^AND |^OR |^\(|^\)|^!|^&&|^\|\|/i; //trailing space on some tokens on purpose.
var sQuery = ""; //will be eval'ed for each i-th candidateItem, based on query components.
var op = "";
var val = "";
var pos = -1;
var err = false;
var key = "";
var value = "";
var tok = "";
pos2 = -1;
for(i = 0; i < arrayOfItems.length; ++i){
var match = true;
var candidateItem = arrayOfItems[i];
if(candidateItem === null){
match = false;
}else{
//process entire string for this i-th candidateItem.
complexQuery = complexQuerySave; //restore query for next candidateItem.
sQuery = "";
//work left to right, finding either key:value pair or logical operator at the beginning of the complexQuery string.
//when found, concatenate to sQuery and remove from complexQuery and loop back.
while(complexQuery.length > 0 && !err){
op = complexQuery.match(begRegExp);
//get/process/append one or two leading logical operators.
while(op && !err){ //look for leading logical operators.
complexQuery = lang.trim(complexQuery.replace(op[0],""));
op = lang.trim(op[0]).toUpperCase();
//convert some logical operators to their javascript equivalents for later eval.
op = op == "NOT" ? "!" : op == "AND" || op == "," ? "&&" : op == "OR" ? "||" : op;
op = " " + op + " ";
sQuery += op;
op = complexQuery.match(begRegExp);
}//end op && !err
//now get/process/append one key:value pair.
if(complexQuery.length > 0){
var opsRegex = /:|>=|<=|>|</g,
matches = complexQuery.match(opsRegex),
match = matches && matches.shift(),
regex;
pos = complexQuery.indexOf(match);
if(pos == -1){
err = true;
break;
}else{
key = lang.trim(complexQuery.substring(0,pos).replace(/\"|\'/g,""));
complexQuery = lang.trim(complexQuery.substring(pos + match.length));
tok = complexQuery.match(/^\'|^\"/); //quoted?
if(tok){
tok = tok[0];
pos = complexQuery.indexOf(tok);
pos2 = complexQuery.indexOf(tok,pos + 1);
if(pos2 == -1){
err = true;
break;
}
value = complexQuery.substring(pos + match.length,pos2);
if(pos2 == complexQuery.length - 1){ //quote is last character
complexQuery = "";
}else{
complexQuery = lang.trim(complexQuery.substring(pos2 + 1));
}
if (match != ':') {
regex = this.getValue(candidateItem, key) + match + value;
} else {
regex = filterUtil.patternToRegExp(value, ignoreCase);
}
sQuery += this._containsValue(candidateItem, key, value, regex);
}
else{ //not quoted, so a space, comma, or closing parens (or the end) will be the break.
tok = complexQuery.match(/\s|\)|,/);
if(tok){
var pos3 = new Array(tok.length);
for(var j = 0;j<tok.length;j++){
pos3[j] = complexQuery.indexOf(tok[j]);
}
pos = pos3[0];
if(pos3.length > 1){
for(var j=1;j<pos3.length;j++){
pos = Math.min(pos,pos3[j]);
}
}
value = lang.trim(complexQuery.substring(0,pos));
complexQuery = lang.trim(complexQuery.substring(pos));
}else{ //not a space, so must be at the end of the complexQuery.
value = lang.trim(complexQuery);
complexQuery = "";
} //end inner if(tok) else
if (match != ':') {
regex = this.getValue(candidateItem, key) + match + value;
} else {
regex = filterUtil.patternToRegExp(value, ignoreCase);
console.log("regex value: ", value, " regex pattern: ", regex);
}
sQuery += this._containsValue(candidateItem, key, value, regex);
} //end outer if(tok) else
} //end found ":"
} //end if(complexQuery.length > 0)
} //end while complexQuery.length > 0 && !err, so finished the i-th item.
match = eval(sQuery);
} //end else is non-null candidateItem.
if(match){
items.push(candidateItem);
}
} //end for/next of all items.
if(err){
//soft fail.
items = [];
console.log("The store's _fetchItems failed, probably due to a syntax error in query.");
}
}else{
// No query...
// We want a copy to pass back in case the parent wishes to sort the array.
// We shouldn't allow resort of the internal list, so that multiple callers
// can get lists and sort without affecting each other. We also need to
// filter out any null values that have been left as a result of deleteItem()
// calls in ItemFileWriteStore.
for(var i = 0; i < arrayOfItems.length; ++i){
var item = arrayOfItems[i];
if(item !== null){
items.push(item);
}
}
} //end if there is a query.
findCallback(items, requestArgs);
} //end filter function
});
});