JS True is bool or string, testing both
[myslice.git] / manifoldapi / static / js / manifold-query.js
1 var guid = (function() {
2   function s4() {
3     return Math.floor((1 + Math.random()) * 0x10000)
4                .toString(16)
5                .substring(1);
6   }
7   return function() {
8     return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
9            s4() + '-' + s4() + s4() + s4();
10   };
11 })();
12
13 function ManifoldQuery(action, object, timestamp, filters, params, fields, unique, query_uuid, aq, sq) {  
14     // get, update, delete, create
15     var action;
16     // slice, user, network... 
17     var object; 
18     // timestamp, now, latest(cache) : date of the results queried    
19     var timestamp;
20     // key(field),op(=<>),value
21     var filters;
22     // todo
23     var params;
24     // hostname, ip,... 
25     var fields;
26     // 0,1 : list of element of an object or single object  
27     var unique;
28     // query_uuid : unique identifier of a query
29     var query_uuid;
30     // Query : root query (no sub-Query)
31     var analyzed_query;
32     // {} : Assoc Table of sub-queries ["resources"->subQ1, "users"->subQ2]
33     var subqueries;
34
35 /*-------------------------------------------------------------
36               Query properties are SQL like : 
37 ---------------------------------------------------------------
38 SELECT fields FROM object WHERE filter;
39 UPDATE object SET field=value WHERE filter; / returns SELECT 
40 DELETE FROM object WHERE filter
41 INSERT INTO object VALUES(field=value)
42 -------------------------------------------------------------*/
43     
44     this.__repr = function () {
45         res  = "ManifoldQuery ";
46 //      res += " id=" + this.query_uuid;
47         res += " a=" + this.action;
48         res += " o=" + this.object;
49         res += " ts=" + this.timestamp;
50         res += " flts=" + this.filters;
51         res += " flds=" + this.fields;
52         res += " prms=" + this.params;
53         return res;
54     }   
55
56     this.clone = function() {
57         // 
58         var q = new ManifoldQuery();
59         q.action     = this.action;
60         q.object     = this.object;
61         q.timestamp  = this.timestamp;
62         q.filters    = this.filters.slice();
63         q.fields     = this.fields.slice();
64         q.query_uuid = this.query_uuid;
65
66         if (this.analyzed_query)
67             q.analyzed_query = this.analyzed_query.clone();
68         else
69             q.analyzed_query = null;
70
71         if (this.subqueries) {
72             q.subqueries = {}
73             for (method in this.subqueries)
74                 q.subqueries[method] = this.subqueries[method].clone();
75         }
76
77         // deep extend not working for custom objects
78         // $.extend(true, q, this);
79         return q;
80     }
81
82     this.add_filter = function(key, op, value) {
83         this.filters.push(new Array(key, op, value));
84     }
85     this.update_filter = function(key, op, value) {
86         // Need to be improved...
87         // remove all occurrences of key if operation is not defined
88         if(!op){
89             this.filters = jQuery.grep(this.filters, function(val, i) {
90                 return val[0] != key; 
91             });
92         // Else remove the key+op filters
93         }else{
94             this.filters = jQuery.grep(this.filters, function(val, i) {return (val[0] != key || val[1] != op);});
95         }
96         this.filters.push(new Array(key, op, value));
97     }
98
99     this.remove_filter = function (key,op,value) {
100         // if operator is null then remove all occurences of this key
101         if(!op){
102             this.filters = jQuery.grep(this.filters, function(val, i) { 
103                 return val[0] != key; 
104             });
105         }else{
106             this.filters = jQuery.grep(this.filters, function(val, i) {return (val[0] != key || val[1] != op);});
107         }
108     }
109
110     // FIXME These functions computing diff's between queries are meant to be shared
111     this.diff_fields = function(otherQuery) {
112         var f1 = this.fields;
113         var f2 = otherQuery.fields;
114
115         /* added elements are the ones in f2 not in f1 */
116         var added   = jQuery.grep(f2, function (x) { return jQuery.inArray(x, f1) == -1 }); 
117         /* removed elements are the ones in f1 not in f2 */
118         var removed = jQuery.grep(f1, function (x) { return jQuery.inArray(x, f2) == -1 }); 
119         
120         return {'added':added, 'removed':removed};
121     }
122
123     // FIXME Modify filter to filters
124     this.diff_filter = function(otherQuery) {
125         var f1 = this.filters;
126         var f2 = otherQuery.filters;
127         
128         /* added elements are the ones in f2 not in f1 */
129         var added   = jQuery.grep(f2, function (x) { return !arrayInArray(x, f1)}); 
130         /* removed elements are the ones in f1 not in f2 */
131         var removed = jQuery.grep(f1, function (x) { return !arrayInArray(x, f2)}); 
132         
133         return {'added':added, 'removed':removed};
134     } 
135
136     // Callaback received 3 parameters: query, data, parent_query
137     this.iter_subqueries = function(callback, data)
138     {
139         rec = function(query, callback, data, parent_query) {
140             callback(query, data, parent_query);
141             jQuery.each(query.subqueries, function(object, subquery) {
142                 rec(subquery, callback, data, query);
143             });
144         };
145
146         if (!!this.analyzed_query)
147             query = this.analyzed_query;
148         else
149             query = this;
150
151         rec(query, callback, data, null);
152     }
153
154     this.select = function(field)
155     {
156         this.fields.push(field);
157     }
158
159     this.unselect = function(field)
160     {   
161         this.fields = $.grep(this.fields, function(x) { return x != field; });
162     }
163
164 // we send queries as a json string now 
165 //    this.as_POST = function() {
166 //        return {'action': this.action, 'object': this.object, 'timestamp': this.timestamp,
167 //              'filters': this.filters, 'params': this.params, 'fields': this.fields};
168 //    }
169     this.analyze_subqueries = function() {
170         /* adapted from the PHP function in com_tophat/includes/query.php */
171         var q = new ManifoldQuery();
172         q.query_uuid = this.query_uuid;
173         q.action = this.action;
174         q.object = this.object;
175         q.timestamp = this.timestamp;
176
177         /* Filters */
178         jQuery.each(this.filters, function(i, filter) {
179             var k = filter[0];
180             var op = filter[1];
181             var v = filter[2];
182             var pos = k.indexOf('.');
183             if (pos != -1) {
184                 var object = k.substr(0, pos);
185                 var field = k.substr(pos+1);
186                 if (!q.subqueries[object]) {
187                     q.subqueries[object] = new ManifoldQuery();
188                     q.subqueries[object].action = q.action;
189                     q.subqueries[object].object = object;
190                     q.subqueries[object].timestamp = q.timestamp;
191                 }
192                 q.subqueries[object].filters.push(Array(field, op, v));
193             } else {
194                 q.filters.push(filter);
195             }
196         });
197
198         /* Params */
199         jQuery.each(this.params, function(param, value) {
200             var pos = param.indexOf('.');
201             if (pos != -1) {
202                 var object = param.substr(0, pos);
203                 var field = param.substr(pos+1);
204                 if (!q.subqueries[object]) {
205                     q.subqueries[object] = new ManifoldQuery();
206                     q.subqueries[object].action = q.action;
207                     q.subqueries[object].object = object;
208                     q.subqueries[object].timestamp = q.timestamp;
209                 }
210                 q.subqueries[object].params[field] = value;
211             } else {
212                 q.params[field] = value;
213             }
214         });
215
216         /* Fields */
217         jQuery.each(this.fields, function(i, v) {
218             var pos = v.indexOf('.');
219             if (pos != -1) {
220                 var object = v.substr(0, pos);
221                 var field = v.substr(pos+1);
222                 if (!q.subqueries[object]) {
223                     q.subqueries[object] = new ManifoldQuery();
224                     q.subqueries[object].action = q.action;
225                     q.subqueries[object].object = object;
226                     q.subqueries[object].timestamp = q.timestamp;
227                 }
228                 q.subqueries[object].fields.push(field);
229             } else {
230                 q.fields.push(v);
231             }
232         });
233         this.analyzed_query = q;
234     }
235  
236     /* constructor */
237     if (typeof action == "undefined")
238         this.action = "get";
239     else
240         this.action = action;
241     
242     if (typeof object == "undefined")
243         this.object = null;
244     else
245         this.object = object;
246
247     if (typeof timestamp == "undefined")
248         this.timestamp = "now";
249     else
250         this.timestamp = timestamp;
251
252     if (typeof filters == "undefined")
253         this.filters = [];
254     else
255         this.filters = filters;
256
257     if (typeof params == "undefined")
258         this.params = {};
259     else
260         this.params = params;
261
262     if (typeof fields == "undefined")
263         this.fields = [];
264     else
265         this.fields = fields;
266
267     if (typeof unique == "undefined")
268         this.unique = false;
269     else
270         this.unique = unique;
271
272     if (typeof unique == "undefined")
273         this.query_uuid = guid();
274     else
275         this.query_uuid = query_uuid;
276
277     if (typeof aq == "undefined")
278         this.analyzed_query = null;
279     else
280         this.analyzed_query = aq;
281
282     if (typeof sq == "undefined")
283         this.subqueries = {};
284     else
285         this.subqueries = sq;
286 }