|
1 Db = function() { |
|
2 //initial comment db as objs (TREE LIKE MODEL : replies are included in comment.replies) |
|
3 this.comments = null; // current set of (filtered) comments |
|
4 this.allComments = null; // all server database comments |
|
5 |
|
6 // obj |
|
7 // keys : commentDbId as string |
|
8 // values : comment db as obj |
|
9 this.commentsByDbId = {}; |
|
10 this.allCommentsByDbId = {}; |
|
11 |
|
12 // dictionary (always contains all comments (no reply) whatever the filter |
|
13 // order key --> ordered array of comment ids (no reply) |
|
14 this.ordered_comment_ids = {}; // all server database comments |
|
15 |
|
16 } |
|
17 |
|
18 Db.prototype = { |
|
19 |
|
20 ////////////////////////////// |
|
21 // CORE FUNCTIONS |
|
22 ////////////////////////////// |
|
23 init : function() { |
|
24 // at first server side ordered comment by asc ids, replies by creation date : |
|
25 this.allComments = CY.JSON.parse(sv_comments) ; |
|
26 this._computeAllCommentsByDbId() ; |
|
27 |
|
28 this._reorder() ; |
|
29 }, |
|
30 |
|
31 _del : function (arr, dic, id) { |
|
32 // first recursively remove all replies |
|
33 var comment = dic[id] ; |
|
34 |
|
35 for (var i = 0 ; i < comment.replies.length ; i++) { |
|
36 var rid = comment.replies[i].id ; |
|
37 this._del(comment.replies, dic, rid) ; |
|
38 i--; |
|
39 } |
|
40 |
|
41 for (var i = 0, ilen = arr.length ; i < ilen ; i++) { |
|
42 if (arr[i].id == id) { |
|
43 arr.splice(i, 1) ; |
|
44 delete dic[id] ; |
|
45 break ; |
|
46 } |
|
47 } |
|
48 }, |
|
49 |
|
50 del : function(comment) { |
|
51 var arr = (comment.reply_to_id == null) ? this.comments : this.commentsByDbId[comment.reply_to_id].replies ; |
|
52 this._del(arr, this.commentsByDbId, comment.id) ; |
|
53 arr = (comment.reply_to_id == null) ? this.allComments : this.allCommentsByDbId[comment.reply_to_id].replies ; |
|
54 this._del(arr, this.allCommentsByDbId, comment.id) ; |
|
55 |
|
56 this._reorder() ; |
|
57 |
|
58 }, |
|
59 |
|
60 // maintains the ordered lists |
|
61 _reorder : function() { |
|
62 |
|
63 // scope (order by start_wrapper, start_offset, end_wrapper, end_offset |
|
64 var a = [] ; |
|
65 |
|
66 for (var i = 0, ilen = this.allComments.length ; i < ilen ; i++) { |
|
67 |
|
68 var comment = this.allComments[i] ; |
|
69 var found = false ; |
|
70 |
|
71 for (var j = 0, jlen = a.length ; j < jlen ; j++) { |
|
72 |
|
73 var id = a[j] ; |
|
74 var comment2 = this.allCommentsByDbId[id] ; |
|
75 |
|
76 if ((comment.start_wrapper < comment2.start_wrapper) |
|
77 || |
|
78 ((comment.start_wrapper == comment2.start_wrapper) && (comment.start_offset < comment2.start_offset) ) |
|
79 || |
|
80 ((comment.start_wrapper == comment2.start_wrapper) && (comment.start_offset == comment2.start_offset) && (comment.end_wrapper < comment2.end_wrapper) ) |
|
81 || |
|
82 ((comment.start_wrapper == comment2.start_wrapper) && (comment.start_offset == comment2.start_offset) && (comment.end_wrapper == comment2.end_wrapper) && (comment.end_offset < comment2.end_offset) ) ) { |
|
83 a.splice(j, 0, comment.id) ; |
|
84 found = true ; |
|
85 break ; |
|
86 } |
|
87 } |
|
88 if (!found) { |
|
89 a.push(comment.id) ; |
|
90 } |
|
91 } |
|
92 this.ordered_comment_ids['scope'] = a ; |
|
93 |
|
94 // modified thread |
|
95 a = [] ; |
|
96 var mod = {} ; // we'll aggregate modification dates in this assoc array id --> latest modification |
|
97 |
|
98 for (var i = 0, ilen = this.allComments.length ; i < ilen ; i++) { |
|
99 |
|
100 var comment = this.allComments[i] ; |
|
101 var commentModif = comment.modified ; |
|
102 |
|
103 mod[comment.id] = commentModif ; |
|
104 |
|
105 for (var j = 0, jlen = comment.replies.length ; j < jlen ; j++) { |
|
106 |
|
107 var reply = comment.replies[j] ; |
|
108 var replyModif = reply.modified ; |
|
109 |
|
110 if (replyModif > mod[comment.id]) |
|
111 mod[comment.id] = replyModif ; |
|
112 } |
|
113 } |
|
114 |
|
115 for (var id in mod) { |
|
116 var numberId = this.allCommentsByDbId[id].id |
|
117 var found = false ; |
|
118 for (var i = 0, ilen = a.length ; i < ilen ; i++) { |
|
119 var id2 = a[i] ; |
|
120 |
|
121 if (mod[id] < mod[id2]) { |
|
122 a.splice(i, 0, numberId) ; |
|
123 found = true ; |
|
124 break ; |
|
125 } |
|
126 |
|
127 } |
|
128 if (!found) { |
|
129 a.push(numberId) ; |
|
130 } |
|
131 } |
|
132 |
|
133 this.ordered_comment_ids['modif_thread'] = a ; |
|
134 }, |
|
135 |
|
136 // EDIT OR ADD CASE : when just added id is max and so both (comments and replies) initial id asc order remains |
|
137 _upd : function(arr, dic, c) { |
|
138 var found = false ; |
|
139 for (var i = 0, ilen = arr.length ; i < ilen ; i++) { |
|
140 if (arr[i].id == c.id) { // edit |
|
141 arr.splice(i, 1, c) ; |
|
142 found = true ; |
|
143 break ; |
|
144 } |
|
145 } |
|
146 |
|
147 if (!found) { // add |
|
148 arr.push(c) ; |
|
149 } |
|
150 |
|
151 dic[c.id] = c ; |
|
152 }, |
|
153 |
|
154 // EDIT OR ADD CASE : when just added id is max and so both (comments and replies) initial id asc order respected |
|
155 upd : function(comment) { |
|
156 var arr = (comment.reply_to_id == null) ? this.allComments : this.allCommentsByDbId[comment.reply_to_id].replies ; |
|
157 this._upd(arr, this.allCommentsByDbId, comment) ; |
|
158 |
|
159 var cloneComment = CY.clone(comment) ; |
|
160 |
|
161 arr = (comment.reply_to_id == null) ? this.comments : this.commentsByDbId[comment.reply_to_id].replies ; |
|
162 this._upd(arr, this.commentsByDbId, cloneComment) ; |
|
163 |
|
164 this._reorder() ; |
|
165 |
|
166 }, |
|
167 |
|
168 // initializes this.comments |
|
169 // commentId is the result of a computeFilterResults call : no assumption can be made on the order of ids (!) |
|
170 // so we'll loop through allComments to carry order from allComments to comments |
|
171 initComments : function(commentIds) { |
|
172 this.comments = [] ; |
|
173 for (var i = 0, ilen = this.allComments.length ; i < ilen ; i++) { |
|
174 |
|
175 var index = CY.Array.indexOf(commentIds, this.allComments[i].id) ; |
|
176 if (index != -1) { |
|
177 |
|
178 var cloneComment = CY.clone(this.allComments[i]) ; |
|
179 |
|
180 this.comments.push(cloneComment) ; |
|
181 } |
|
182 } |
|
183 this._computeCommentsByDbId() ; |
|
184 }, |
|
185 |
|
186 _computeCommentsByDbId : function() { |
|
187 this.commentsByDbId = {} ; |
|
188 var flatComments = this.getThreads(this.comments) ; |
|
189 for ( var i = 0; i < flatComments.length; i++) |
|
190 this.commentsByDbId[flatComments[i].id] = flatComments[i]; |
|
191 }, |
|
192 |
|
193 _computeAllCommentsByDbId : function() { |
|
194 this.allCommentsByDbId = {} ; |
|
195 var flatComments = this.getThreads(this.allComments) ; |
|
196 for (var i = 0; i < flatComments.length; i++) |
|
197 this.allCommentsByDbId[flatComments[i].id] = flatComments[i]; |
|
198 }, |
|
199 |
|
200 // returns threads : |
|
201 // given an array [comment1, comment2, comment3], this function will return [comment1, comment1reply1, comment1reply1reply1, comment1reply1reply2, comment2, comment3, comment3reply1] |
|
202 //note : will return top parents ordered the way comments are |
|
203 getThreads : function(comments) { |
|
204 var ret = [] ; |
|
205 |
|
206 for (var i = 0 ; i < comments.length ; i++) { |
|
207 ret.push(comments[i]) ; |
|
208 if (comments[i].replies.length > 0) |
|
209 ret = ret.concat(this.getThreads(comments[i].replies)) ; |
|
210 } |
|
211 return ret ; |
|
212 }, |
|
213 _getPath : function(dic, comment) { |
|
214 var ret = [comment] ; |
|
215 |
|
216 var c = comment ; |
|
217 while (c.reply_to_id != null) { |
|
218 c = dic[c.reply_to_id] ; |
|
219 ret.push(c) ; |
|
220 } |
|
221 |
|
222 return ret ; |
|
223 }, |
|
224 // returns comments as array : [comment, ..., comment's top parent] |
|
225 getPath : function(comment) { |
|
226 return this._getPath(this.commentsByDbId, comment) ; |
|
227 }, |
|
228 // getCommentFromIComment ... |
|
229 getComment : function(dbId) { |
|
230 return this.commentsByDbId[dbId] ; |
|
231 }, |
|
232 |
|
233 getCommentByKey : function(key) { |
|
234 for (var id in this.commentsByDbId) { |
|
235 var comment = this.commentsByDbId[id] ; |
|
236 if (comment.key == key) { |
|
237 return comment ; |
|
238 } |
|
239 } |
|
240 return null ; |
|
241 }, |
|
242 |
|
243 isChild : function(commentDbId, parentDbId) { |
|
244 var comment = this.commentsByDbId[commentDbId] ; |
|
245 |
|
246 var isChild = (commentDbId == parentDbId) ; |
|
247 |
|
248 while ((!isChild) && (comment.reply_to_id != null)) { |
|
249 comment = this.commentsByDbId[comment.reply_to_id] ; |
|
250 isChild = (comment.id == parentDbId) ; ; |
|
251 } |
|
252 return isChild ; |
|
253 }, |
|
254 |
|
255 ////////////////////////////// |
|
256 // BROWSING FUNCTIONS |
|
257 ////////////////////////////// |
|
258 |
|
259 browsingIndex : function(dbId) { |
|
260 var indx = {} ; |
|
261 for (var order in this.ordered_comment_ids) { |
|
262 var inFilter = CY.Array.filter(this.ordered_comment_ids[order], function(id) {return (id in this.commentsByDbId);}, this) ; |
|
263 indx[order] = CY.Array.indexOf(inFilter, dbId ) ; |
|
264 } |
|
265 //indx['total'] = this.ordered_comment_ids['scope'].length |
|
266 return indx ; |
|
267 }, |
|
268 |
|
269 browse : function(order, whereto, dbId) { |
|
270 //var arr = this.ordered_comment_ids[gConf['defaultBrowsingOrder']] ; |
|
271 // CY.log(order) ; |
|
272 var arr = this.ordered_comment_ids[order] ; |
|
273 if (arr.length > 0) { |
|
274 |
|
275 var starti = -1 ; |
|
276 if ((whereto == 'prev') || (whereto == 'next')) { |
|
277 |
|
278 for (var i = 0 ; i < arr.length ; i++) { |
|
279 var id = arr[i] ; |
|
280 if (id == dbId) { |
|
281 starti = (whereto == 'prev') ? i - 1 : i + 1 ; |
|
282 starti = (arr.length + starti) % arr.length ; // to guaranty a positive value |
|
283 break ; |
|
284 } |
|
285 } |
|
286 if (starti == -1) { |
|
287 CY.error("internal error in db browse (was called with a dbId that isn't among the filtered ones)") ; |
|
288 return null; |
|
289 } |
|
290 } |
|
291 if (whereto == 'last') { |
|
292 starti = arr.length - 1 ; |
|
293 } |
|
294 if (whereto == 'first') { |
|
295 starti = 0 ; |
|
296 } |
|
297 |
|
298 for (var i = starti, j = 0 ; (i >= 0) && (i < arr.length) ; j++ ) { |
|
299 var id = arr[i] ; |
|
300 if (id in this.commentsByDbId) // checking id is among the filtered ones |
|
301 return this.commentsByDbId[id] ; |
|
302 if ((whereto == 'prev') || (whereto == 'last')) |
|
303 i = i - 1 ; |
|
304 else |
|
305 i = i + 1 ; |
|
306 i = (arr.length + i) % arr.length ; // to guaranty a positive value |
|
307 if (j > arr.length)// to prevent an infinite loop |
|
308 break ; |
|
309 } |
|
310 |
|
311 CY.error("internal error in db browse (could not find any filtered comment)") ; |
|
312 } |
|
313 return null; |
|
314 }, |
|
315 |
|
316 ////////////////////////////// |
|
317 // FILTER FUNCTIONS |
|
318 ////////////////////////////// |
|
319 |
|
320 //returns the list of commentIds satisfying the filter |
|
321 computeFilterResults : function(filterGETValues) { |
|
322 var filterData = {} ; |
|
323 if (filterGETValues) { |
|
324 for (key in filterGETValues) { |
|
325 if (key.indexOf('filter_') == 0) |
|
326 filterData[key.substr('filter_'.length)] = filterGETValues[key]; |
|
327 } |
|
328 } |
|
329 else { |
|
330 if (gLayout.isInFrame()) |
|
331 filterData = parent.f_getFrameFilterData() ; |
|
332 } |
|
333 |
|
334 var cWithNameIds = [] ; |
|
335 var rWithNameIds = [] ; |
|
336 var filterName = "" ; |
|
337 if ('name' in filterData) |
|
338 filterName = filterData['name'] ; |
|
339 this.filterByName(filterName, cWithNameIds, rWithNameIds) ; |
|
340 |
|
341 var cAfterDateIds = [] ; |
|
342 var rAfterDateIds = [] ; |
|
343 var filterDate = "" ; |
|
344 if ('date' in filterData) |
|
345 filterDate = filterData['date'] ; |
|
346 this.filterByDate(filterDate, cAfterDateIds, rAfterDateIds) ; |
|
347 |
|
348 var cWithTextIds = [] ; |
|
349 var rWithTextIds = [] ; |
|
350 var filterText = "" ; |
|
351 if ('text' in filterData) |
|
352 filterText = filterData['text'] ; |
|
353 this.filterByText(filterText, cWithTextIds, rWithTextIds) ; |
|
354 |
|
355 var cWithTagIds = [] ; |
|
356 var rWithTagIds = [] ; |
|
357 var filterTag = "" ; |
|
358 if ('tag' in filterData) |
|
359 filterTag = filterData['tag'] ; |
|
360 this.filterByTag(filterTag, cWithTagIds, rWithTagIds) ; |
|
361 |
|
362 var cWithStateIds = [] ; |
|
363 var rWithStateIds = [] ; |
|
364 var filterState = "" ; |
|
365 if ('state' in filterData) |
|
366 filterState = filterData['state'] ; |
|
367 this.filterByState(filterState, cWithStateIds, rWithStateIds) ; |
|
368 |
|
369 |
|
370 var commentIds = [] ; |
|
371 var replyIds = [] ; |
|
372 // find intersections |
|
373 for (var i = 0, ilen = cWithNameIds.length ; i < ilen ; i++) { |
|
374 var id = cWithNameIds[i] ; |
|
375 if ((CY.Array.indexOf(cAfterDateIds, id) != -1) && (CY.Array.indexOf(cWithTextIds,id) != -1) && (CY.Array.indexOf(cWithTagIds,id) != -1) && (CY.Array.indexOf(cWithStateIds,id) != -1)) { |
|
376 commentIds.push(id) ; |
|
377 } |
|
378 } |
|
379 |
|
380 for (var i = 0, ilen = rWithNameIds.length ; i < ilen ; i++) { |
|
381 var id = rWithNameIds[i] ; |
|
382 if ((CY.Array.indexOf(rAfterDateIds,id) != -1) && (CY.Array.indexOf(rWithTextIds,id) != -1) && (CY.Array.indexOf(rWithTagIds,id) != -1) && (CY.Array.indexOf(rWithStateIds,id) != -1)) { |
|
383 replyIds.push(id) ; |
|
384 } |
|
385 } |
|
386 |
|
387 var nbReplies = replyIds.length, nbComments = commentIds.length ; |
|
388 var nbDiscussions = nbComments ; |
|
389 |
|
390 // look for comments to add because a reply satisfies the filter |
|
391 // CY.log('replyIds:') ; |
|
392 // CY.log(replyIds) ; |
|
393 // CY.log('this.allCommentsByDbId :');CY.A |
|
394 // CY.log(this.allCommentsByDbId); |
|
395 for (var i = 0, ilen = replyIds.length ; i < ilen ; i++) { |
|
396 var id = replyIds[i] ; |
|
397 var reply = this.allCommentsByDbId[id] ; |
|
398 var parents = this._getPath(this.allCommentsByDbId, reply) ; |
|
399 var topComment = parents[parents.length - 1] ; |
|
400 var id = topComment.id ; |
|
401 if (CY.Array.indexOf(commentIds,id) == -1) { |
|
402 commentIds.push(id) ; |
|
403 nbDiscussions++ ; |
|
404 } |
|
405 } |
|
406 |
|
407 return {'commentIds': commentIds,'nbDiscussions':nbDiscussions, 'nbComments':nbComments, 'nbReplies':nbReplies} ; |
|
408 }, |
|
409 |
|
410 filterByText : function(text, cWithTextIds, rWithTextIds) { |
|
411 var re = new RegExp(text, "gi"); |
|
412 for (var id in this.allCommentsByDbId) { |
|
413 var comment = this.allCommentsByDbId[id] ; |
|
414 if (text == "" || re.exec(comment.title) != null || re.exec(comment.content) != null) { // search only in the comment (not the comment scope) for now |
|
415 if (comment.reply_to_id == null) |
|
416 cWithTextIds.push(comment.id); |
|
417 else |
|
418 rWithTextIds.push(comment.id) ; |
|
419 } |
|
420 } |
|
421 }, |
|
422 |
|
423 filterByName : function(name, cWithNameIds, rWithNameIds) { |
|
424 for (var id in this.allCommentsByDbId) { |
|
425 var comment = this.allCommentsByDbId[id] ; |
|
426 if (name == "" || comment.name == name) { // sensitive exact match for now |
|
427 if (comment.reply_to_id == null) |
|
428 cWithNameIds.push(comment.id); |
|
429 else |
|
430 rWithNameIds.push(comment.id) ; |
|
431 } |
|
432 } |
|
433 }, |
|
434 |
|
435 // warning : tags are case sensitive |
|
436 filterByTag : function(tag, cWithTagIds, rWithTagIds) { |
|
437 // cf ", ".join... in client.py |
|
438 var re0 = new RegExp("^" + tag + "$", "g"); |
|
439 var re1 = new RegExp("^" + tag + ", ", "g"); |
|
440 var re2 = new RegExp(", " + tag + ", ", "g"); |
|
441 var re3 = new RegExp(", " + tag + "$", "g"); |
|
442 for (var id in this.allCommentsByDbId) { |
|
443 var comment = this.allCommentsByDbId[id] ; |
|
444 if (tag == "" || re0.exec(comment.tags) || re1.exec(comment.tags) != null || re2.exec(comment.tags) != null || re3.exec(comment.tags) != null) { // search only in the comment (not the comment scope) for now |
|
445 if (comment.reply_to_id == null) |
|
446 cWithTagIds.push(comment.id); |
|
447 else |
|
448 rWithTagIds.push(comment.id) ; |
|
449 } |
|
450 } |
|
451 }, |
|
452 |
|
453 filterByState : function(state, cWithStateIds, rWithStateIds) { |
|
454 for (var id in this.allCommentsByDbId) { |
|
455 var comment = this.allCommentsByDbId[id] ; |
|
456 if (state == "" || comment.state == state) { |
|
457 if (comment.reply_to_id == null) |
|
458 cWithStateIds.push(comment.id); |
|
459 else |
|
460 rWithStateIds.push(comment.id) ; |
|
461 } |
|
462 } |
|
463 }, |
|
464 |
|
465 filterByDate : function(date_str, cAfterDateIds, rAfterDateIds) { |
|
466 var date = (date_str == "") ? 0 : parseInt(date_str) ; |
|
467 for (var id in this.allCommentsByDbId) { |
|
468 var comment = this.allCommentsByDbId[id] ; |
|
469 if (comment.modified > date) { |
|
470 if (comment.reply_to_id == null) |
|
471 cAfterDateIds.push(comment.id); |
|
472 else |
|
473 rAfterDateIds.push(comment.id) ; |
|
474 } |
|
475 } |
|
476 }, |
|
477 // filterByDate : function(date_str, cAfterDateIds, rAfterDateIds) { |
|
478 // var date = (date_str == "") ? "" : Date.parseDate(date_str, sv_client_date_fmt).getTime() ; |
|
479 // for (var id in this.allCommentsByDbId) { |
|
480 // var comment = this.allCommentsByDbId[id] ; |
|
481 // // TODO : created should be the date not a string !! |
|
482 // var create_date = (date_str == "") ? "" : Date.parseDate(comment.created_str, sv_client_date_fmt).getTime() ; |
|
483 // if (date_str == "" || create_date > date) { |
|
484 // if (comment.reply_to_id == null) |
|
485 // cAfterDateIds.push(comment.id); |
|
486 // else |
|
487 // rAfterDateIds.push(comment.id) ; |
|
488 // } |
|
489 // } |
|
490 // }, |
|
491 |
|
492 ////////////////////////////// |
|
493 // COUNT FUNCTIONS |
|
494 ////////////////////////////// |
|
495 |
|
496 getCommentsAndRepliesCounts : function(all) { |
|
497 var cCount = 0 ; |
|
498 var rCount = 0 ; |
|
499 var arr = (all) ? this.allComments:this.comments; |
|
500 var flatComments = this.getThreads(arr) ; |
|
501 for ( var i = 0; i < flatComments.length; i++) { |
|
502 if (flatComments[i].reply_to_id == null) |
|
503 cCount++ ; |
|
504 else |
|
505 rCount++ ; |
|
506 } |
|
507 return [cCount, rCount] ; |
|
508 }, |
|
509 |
|
510 // counts both comments and comments |
|
511 getCommentsNb : function(all) { |
|
512 var arr = (all) ? this.allComments:this.comments; |
|
513 return this.getThreads(arr).length ; |
|
514 }, |
|
515 getFilteredCommentIdsAsString : function() { |
|
516 var ret = "" ; |
|
517 for (var id in this.commentsByDbId) |
|
518 ret = ret + id + "," ; |
|
519 return ret ; |
|
520 } |
|
521 } |
|
522 |
|
523 |