1
|
|
|
/* |
2
|
|
|
* websupport.js |
3
|
|
|
* ~~~~~~~~~~~~~ |
4
|
|
|
* |
5
|
|
|
* sphinx.websupport utilties for all documentation. |
6
|
|
|
* |
7
|
|
|
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS. |
8
|
|
|
* :license: BSD, see LICENSE for details. |
9
|
|
|
* |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
(function($) { |
13
|
|
|
$.fn.autogrow = function() { |
14
|
|
|
return this.each(function() { |
15
|
|
|
var textarea = this; |
16
|
|
|
|
17
|
|
|
$.fn.autogrow.resize(textarea); |
18
|
|
|
|
19
|
|
|
$(textarea) |
20
|
|
|
.focus(function() { |
21
|
|
|
textarea.interval = setInterval(function() { |
22
|
|
|
$.fn.autogrow.resize(textarea); |
23
|
|
|
}, 500); |
24
|
|
|
}) |
25
|
|
|
.blur(function() { |
26
|
|
|
clearInterval(textarea.interval); |
27
|
|
|
}); |
28
|
|
|
}); |
29
|
|
|
}; |
30
|
|
|
|
31
|
|
|
$.fn.autogrow.resize = function(textarea) { |
32
|
|
|
var lineHeight = parseInt($(textarea).css('line-height'), 10); |
33
|
|
|
var lines = textarea.value.split('\n'); |
34
|
|
|
var columns = textarea.cols; |
35
|
|
|
var lineCount = 0; |
36
|
|
|
$.each(lines, function() { |
37
|
|
|
lineCount += Math.ceil(this.length / columns) || 1; |
38
|
|
|
}); |
39
|
|
|
var height = lineHeight * (lineCount + 1); |
40
|
|
|
$(textarea).css('height', height); |
41
|
|
|
}; |
42
|
|
|
})(jQuery); |
43
|
|
|
|
44
|
|
|
(function($) { |
45
|
|
|
var comp, by; |
46
|
|
|
|
47
|
|
|
function init() { |
48
|
|
|
initEvents(); |
49
|
|
|
initComparator(); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
function initEvents() { |
53
|
|
|
$(document).on("click", 'a.comment-close', function(event) { |
54
|
|
|
event.preventDefault(); |
55
|
|
|
hide($(this).attr('id').substring(2)); |
56
|
|
|
}); |
57
|
|
|
$(document).on("click", 'a.vote', function(event) { |
58
|
|
|
event.preventDefault(); |
59
|
|
|
handleVote($(this)); |
60
|
|
|
}); |
61
|
|
|
$(document).on("click", 'a.reply', function(event) { |
62
|
|
|
event.preventDefault(); |
63
|
|
|
openReply($(this).attr('id').substring(2)); |
64
|
|
|
}); |
65
|
|
|
$(document).on("click", 'a.close-reply', function(event) { |
66
|
|
|
event.preventDefault(); |
67
|
|
|
closeReply($(this).attr('id').substring(2)); |
68
|
|
|
}); |
69
|
|
|
$(document).on("click", 'a.sort-option', function(event) { |
70
|
|
|
event.preventDefault(); |
71
|
|
|
handleReSort($(this)); |
72
|
|
|
}); |
73
|
|
|
$(document).on("click", 'a.show-proposal', function(event) { |
74
|
|
|
event.preventDefault(); |
75
|
|
|
showProposal($(this).attr('id').substring(2)); |
76
|
|
|
}); |
77
|
|
|
$(document).on("click", 'a.hide-proposal', function(event) { |
78
|
|
|
event.preventDefault(); |
79
|
|
|
hideProposal($(this).attr('id').substring(2)); |
80
|
|
|
}); |
81
|
|
|
$(document).on("click", 'a.show-propose-change', function(event) { |
82
|
|
|
event.preventDefault(); |
83
|
|
|
showProposeChange($(this).attr('id').substring(2)); |
84
|
|
|
}); |
85
|
|
|
$(document).on("click", 'a.hide-propose-change', function(event) { |
86
|
|
|
event.preventDefault(); |
87
|
|
|
hideProposeChange($(this).attr('id').substring(2)); |
88
|
|
|
}); |
89
|
|
|
$(document).on("click", 'a.accept-comment', function(event) { |
90
|
|
|
event.preventDefault(); |
91
|
|
|
acceptComment($(this).attr('id').substring(2)); |
92
|
|
|
}); |
93
|
|
|
$(document).on("click", 'a.delete-comment', function(event) { |
94
|
|
|
event.preventDefault(); |
95
|
|
|
deleteComment($(this).attr('id').substring(2)); |
96
|
|
|
}); |
97
|
|
|
$(document).on("click", 'a.comment-markup', function(event) { |
98
|
|
|
event.preventDefault(); |
99
|
|
|
toggleCommentMarkupBox($(this).attr('id').substring(2)); |
100
|
|
|
}); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Set comp, which is a comparator function used for sorting and |
105
|
|
|
* inserting comments into the list. |
106
|
|
|
*/ |
107
|
|
|
function setComparator() { |
108
|
|
|
// If the first three letters are "asc", sort in ascending order |
109
|
|
|
// and remove the prefix. |
110
|
|
|
if (by.substring(0,3) == 'asc') { |
111
|
|
|
var i = by.substring(3); |
112
|
|
|
comp = function(a, b) { return a[i] - b[i]; }; |
113
|
|
|
} else { |
114
|
|
|
// Otherwise sort in descending order. |
115
|
|
|
comp = function(a, b) { return b[by] - a[by]; }; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
// Reset link styles and format the selected sort option. |
119
|
|
|
$('a.sel').attr('href', '#').removeClass('sel'); |
120
|
|
|
$('a.by' + by).removeAttr('href').addClass('sel'); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Create a comp function. If the user has preferences stored in |
125
|
|
|
* the sortBy cookie, use those, otherwise use the default. |
126
|
|
|
*/ |
127
|
|
|
function initComparator() { |
128
|
|
|
by = 'rating'; // Default to sort by rating. |
129
|
|
|
// If the sortBy cookie is set, use that instead. |
130
|
|
|
if (document.cookie.length > 0) { |
131
|
|
|
var start = document.cookie.indexOf('sortBy='); |
132
|
|
|
if (start != -1) { |
133
|
|
|
start = start + 7; |
134
|
|
|
var end = document.cookie.indexOf(";", start); |
135
|
|
|
if (end == -1) { |
136
|
|
|
end = document.cookie.length; |
137
|
|
|
by = unescape(document.cookie.substring(start, end)); |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
setComparator(); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Show a comment div. |
146
|
|
|
*/ |
147
|
|
|
function show(id) { |
148
|
|
|
$('#ao' + id).hide(); |
149
|
|
|
$('#ah' + id).show(); |
150
|
|
|
var context = $.extend({id: id}, opts); |
151
|
|
|
var popup = $(renderTemplate(popupTemplate, context)).hide(); |
152
|
|
|
popup.find('textarea[name="proposal"]').hide(); |
153
|
|
|
popup.find('a.by' + by).addClass('sel'); |
154
|
|
|
var form = popup.find('#cf' + id); |
155
|
|
|
form.submit(function(event) { |
156
|
|
|
event.preventDefault(); |
157
|
|
|
addComment(form); |
158
|
|
|
}); |
159
|
|
|
$('#s' + id).after(popup); |
160
|
|
|
popup.slideDown('fast', function() { |
161
|
|
|
getComments(id); |
162
|
|
|
}); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Hide a comment div. |
167
|
|
|
*/ |
168
|
|
|
function hide(id) { |
169
|
|
|
$('#ah' + id).hide(); |
170
|
|
|
$('#ao' + id).show(); |
171
|
|
|
var div = $('#sc' + id); |
172
|
|
|
div.slideUp('fast', function() { |
173
|
|
|
div.remove(); |
174
|
|
|
}); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Perform an ajax request to get comments for a node |
179
|
|
|
* and insert the comments into the comments tree. |
180
|
|
|
*/ |
181
|
|
|
function getComments(id) { |
182
|
|
|
$.ajax({ |
183
|
|
|
type: 'GET', |
184
|
|
|
url: opts.getCommentsURL, |
185
|
|
|
data: {node: id}, |
186
|
|
|
success: function(data, textStatus, request) { |
|
|
|
|
187
|
|
|
var ul = $('#cl' + id); |
188
|
|
|
var speed = 100; |
189
|
|
|
$('#cf' + id) |
190
|
|
|
.find('textarea[name="proposal"]') |
191
|
|
|
.data('source', data.source); |
192
|
|
|
|
193
|
|
|
if (data.comments.length === 0) { |
194
|
|
|
ul.html('<li>No comments yet.</li>'); |
195
|
|
|
ul.data('empty', true); |
196
|
|
|
} else { |
197
|
|
|
// If there are comments, sort them and put them in the list. |
198
|
|
|
var comments = sortComments(data.comments); |
199
|
|
|
speed = data.comments.length * 100; |
200
|
|
|
appendComments(comments, ul); |
201
|
|
|
ul.data('empty', false); |
202
|
|
|
} |
203
|
|
|
$('#cn' + id).slideUp(speed + 200); |
204
|
|
|
ul.slideDown(speed); |
205
|
|
|
}, |
206
|
|
|
error: function(request, textStatus, error) { |
|
|
|
|
207
|
|
|
showError('Oops, there was a problem retrieving the comments.'); |
208
|
|
|
}, |
209
|
|
|
dataType: 'json' |
210
|
|
|
}); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Add a comment via ajax and insert the comment into the comment tree. |
215
|
|
|
*/ |
216
|
|
|
function addComment(form) { |
217
|
|
|
var node_id = form.find('input[name="node"]').val(); |
218
|
|
|
var parent_id = form.find('input[name="parent"]').val(); |
219
|
|
|
var text = form.find('textarea[name="comment"]').val(); |
220
|
|
|
var proposal = form.find('textarea[name="proposal"]').val(); |
221
|
|
|
|
222
|
|
|
if (text == '') { |
223
|
|
|
showError('Please enter a comment.'); |
224
|
|
|
return; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
// Disable the form that is being submitted. |
228
|
|
|
form.find('textarea,input').attr('disabled', 'disabled'); |
229
|
|
|
|
230
|
|
|
// Send the comment to the server. |
231
|
|
|
$.ajax({ |
232
|
|
|
type: "POST", |
233
|
|
|
url: opts.addCommentURL, |
234
|
|
|
dataType: 'json', |
235
|
|
|
data: { |
236
|
|
|
node: node_id, |
237
|
|
|
parent: parent_id, |
238
|
|
|
text: text, |
239
|
|
|
proposal: proposal |
240
|
|
|
}, |
241
|
|
|
success: function(data, textStatus, error) { |
|
|
|
|
242
|
|
|
// Reset the form. |
243
|
|
|
if (node_id) { |
244
|
|
|
hideProposeChange(node_id); |
245
|
|
|
} |
246
|
|
|
form.find('textarea') |
247
|
|
|
.val('') |
248
|
|
|
.add(form.find('input')) |
249
|
|
|
.removeAttr('disabled'); |
250
|
|
|
var ul = $('#cl' + (node_id || parent_id)); |
251
|
|
|
if (ul.data('empty')) { |
252
|
|
|
$(ul).empty(); |
253
|
|
|
ul.data('empty', false); |
254
|
|
|
} |
255
|
|
|
insertComment(data.comment); |
256
|
|
|
var ao = $('#ao' + node_id); |
257
|
|
|
ao.find('img').attr({'src': opts.commentBrightImage}); |
258
|
|
|
if (node_id) { |
259
|
|
|
// if this was a "root" comment, remove the commenting box |
260
|
|
|
// (the user can get it back by reopening the comment popup) |
261
|
|
|
$('#ca' + node_id).slideUp(); |
262
|
|
|
} |
263
|
|
|
}, |
264
|
|
|
error: function(request, textStatus, error) { |
|
|
|
|
265
|
|
|
form.find('textarea,input').removeAttr('disabled'); |
266
|
|
|
showError('Oops, there was a problem adding the comment.'); |
267
|
|
|
} |
268
|
|
|
}); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Recursively append comments to the main comment list and children |
273
|
|
|
* lists, creating the comment tree. |
274
|
|
|
*/ |
275
|
|
|
function appendComments(comments, ul) { |
276
|
|
|
$.each(comments, function() { |
277
|
|
|
var div = createCommentDiv(this); |
278
|
|
|
ul.append($(document.createElement('li')).html(div)); |
279
|
|
|
appendComments(this.children, div.find('ul.comment-children')); |
280
|
|
|
// To avoid stagnating data, don't store the comments children in data. |
281
|
|
|
this.children = null; |
282
|
|
|
div.data('comment', this); |
283
|
|
|
}); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* After adding a new comment, it must be inserted in the correct |
288
|
|
|
* location in the comment tree. |
289
|
|
|
*/ |
290
|
|
|
function insertComment(comment) { |
291
|
|
|
var div = createCommentDiv(comment); |
292
|
|
|
|
293
|
|
|
// To avoid stagnating data, don't store the comments children in data. |
294
|
|
|
comment.children = null; |
295
|
|
|
div.data('comment', comment); |
296
|
|
|
|
297
|
|
|
var ul = $('#cl' + (comment.node || comment.parent)); |
298
|
|
|
var siblings = getChildren(ul); |
299
|
|
|
|
300
|
|
|
var li = $(document.createElement('li')); |
301
|
|
|
li.hide(); |
302
|
|
|
|
303
|
|
|
// Determine where in the parents children list to insert this comment. |
304
|
|
|
for(i=0; i < siblings.length; i++) { |
|
|
|
|
305
|
|
|
if (comp(comment, siblings[i]) <= 0) { |
306
|
|
|
$('#cd' + siblings[i].id) |
307
|
|
|
.parent() |
308
|
|
|
.before(li.html(div)); |
309
|
|
|
li.slideDown('fast'); |
310
|
|
|
return; |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
// If we get here, this comment rates lower than all the others, |
315
|
|
|
// or it is the only comment in the list. |
316
|
|
|
ul.append(li.html(div)); |
317
|
|
|
li.slideDown('fast'); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
function acceptComment(id) { |
321
|
|
|
$.ajax({ |
322
|
|
|
type: 'POST', |
323
|
|
|
url: opts.acceptCommentURL, |
324
|
|
|
data: {id: id}, |
325
|
|
|
success: function(data, textStatus, request) { |
|
|
|
|
326
|
|
|
$('#cm' + id).fadeOut('fast'); |
327
|
|
|
$('#cd' + id).removeClass('moderate'); |
328
|
|
|
}, |
329
|
|
|
error: function(request, textStatus, error) { |
|
|
|
|
330
|
|
|
showError('Oops, there was a problem accepting the comment.'); |
331
|
|
|
} |
332
|
|
|
}); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
function deleteComment(id) { |
336
|
|
|
$.ajax({ |
337
|
|
|
type: 'POST', |
338
|
|
|
url: opts.deleteCommentURL, |
339
|
|
|
data: {id: id}, |
340
|
|
|
success: function(data, textStatus, request) { |
|
|
|
|
341
|
|
|
var div = $('#cd' + id); |
342
|
|
|
if (data == 'delete') { |
343
|
|
|
// Moderator mode: remove the comment and all children immediately |
344
|
|
|
div.slideUp('fast', function() { |
345
|
|
|
div.remove(); |
346
|
|
|
}); |
347
|
|
|
return; |
348
|
|
|
} |
349
|
|
|
// User mode: only mark the comment as deleted |
350
|
|
|
div |
351
|
|
|
.find('span.user-id:first') |
352
|
|
|
.text('[deleted]').end() |
353
|
|
|
.find('div.comment-text:first') |
354
|
|
|
.text('[deleted]').end() |
355
|
|
|
.find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + |
356
|
|
|
', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) |
357
|
|
|
.remove(); |
358
|
|
|
var comment = div.data('comment'); |
359
|
|
|
comment.username = '[deleted]'; |
360
|
|
|
comment.text = '[deleted]'; |
361
|
|
|
div.data('comment', comment); |
362
|
|
|
}, |
363
|
|
|
error: function(request, textStatus, error) { |
|
|
|
|
364
|
|
|
showError('Oops, there was a problem deleting the comment.'); |
365
|
|
|
} |
366
|
|
|
}); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
function showProposal(id) { |
370
|
|
|
$('#sp' + id).hide(); |
371
|
|
|
$('#hp' + id).show(); |
372
|
|
|
$('#pr' + id).slideDown('fast'); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
function hideProposal(id) { |
376
|
|
|
$('#hp' + id).hide(); |
377
|
|
|
$('#sp' + id).show(); |
378
|
|
|
$('#pr' + id).slideUp('fast'); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
function showProposeChange(id) { |
382
|
|
|
$('#pc' + id).hide(); |
383
|
|
|
$('#hc' + id).show(); |
384
|
|
|
var textarea = $('#pt' + id); |
385
|
|
|
textarea.val(textarea.data('source')); |
386
|
|
|
$.fn.autogrow.resize(textarea[0]); |
387
|
|
|
textarea.slideDown('fast'); |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
function hideProposeChange(id) { |
391
|
|
|
$('#hc' + id).hide(); |
392
|
|
|
$('#pc' + id).show(); |
393
|
|
|
var textarea = $('#pt' + id); |
394
|
|
|
textarea.val('').removeAttr('disabled'); |
395
|
|
|
textarea.slideUp('fast'); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
function toggleCommentMarkupBox(id) { |
399
|
|
|
$('#mb' + id).toggle(); |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** Handle when the user clicks on a sort by link. */ |
403
|
|
|
function handleReSort(link) { |
404
|
|
|
var classes = link.attr('class').split(/\s+/); |
405
|
|
|
for (var i=0; i<classes.length; i++) { |
406
|
|
|
if (classes[i] != 'sort-option') { |
407
|
|
|
by = classes[i].substring(2); |
408
|
|
|
} |
409
|
|
|
} |
410
|
|
|
setComparator(); |
411
|
|
|
// Save/update the sortBy cookie. |
412
|
|
|
var expiration = new Date(); |
413
|
|
|
expiration.setDate(expiration.getDate() + 365); |
414
|
|
|
document.cookie= 'sortBy=' + escape(by) + |
|
|
|
|
415
|
|
|
';expires=' + expiration.toUTCString(); |
416
|
|
|
$('ul.comment-ul').each(function(index, ul) { |
417
|
|
|
var comments = getChildren($(ul), true); |
418
|
|
|
comments = sortComments(comments); |
419
|
|
|
appendComments(comments, $(ul).empty()); |
420
|
|
|
}); |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
/** |
424
|
|
|
* Function to process a vote when a user clicks an arrow. |
425
|
|
|
*/ |
426
|
|
|
function handleVote(link) { |
427
|
|
|
if (!opts.voting) { |
428
|
|
|
showError("You'll need to login to vote."); |
429
|
|
|
return; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
var id = link.attr('id'); |
433
|
|
|
if (!id) { |
434
|
|
|
// Didn't click on one of the voting arrows. |
435
|
|
|
return; |
436
|
|
|
} |
437
|
|
|
// If it is an unvote, the new vote value is 0, |
438
|
|
|
// Otherwise it's 1 for an upvote, or -1 for a downvote. |
439
|
|
|
var value = 0; |
440
|
|
|
if (id.charAt(1) != 'u') { |
441
|
|
|
value = id.charAt(0) == 'u' ? 1 : -1; |
442
|
|
|
} |
443
|
|
|
// The data to be sent to the server. |
444
|
|
|
var d = { |
445
|
|
|
comment_id: id.substring(2), |
446
|
|
|
value: value |
447
|
|
|
}; |
448
|
|
|
|
449
|
|
|
// Swap the vote and unvote links. |
450
|
|
|
link.hide(); |
451
|
|
|
$('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id) |
452
|
|
|
.show(); |
453
|
|
|
|
454
|
|
|
// The div the comment is displayed in. |
455
|
|
|
var div = $('div#cd' + d.comment_id); |
456
|
|
|
var data = div.data('comment'); |
457
|
|
|
|
458
|
|
|
// If this is not an unvote, and the other vote arrow has |
459
|
|
|
// already been pressed, unpress it. |
460
|
|
|
if ((d.value !== 0) && (data.vote === d.value * -1)) { |
461
|
|
|
$('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide(); |
462
|
|
|
$('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show(); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
// Update the comments rating in the local data. |
466
|
|
|
data.rating += (data.vote === 0) ? d.value : (d.value - data.vote); |
467
|
|
|
data.vote = d.value; |
468
|
|
|
div.data('comment', data); |
469
|
|
|
|
470
|
|
|
// Change the rating text. |
471
|
|
|
div.find('.rating:first') |
472
|
|
|
.text(data.rating + ' point' + (data.rating == 1 ? '' : 's')); |
473
|
|
|
|
474
|
|
|
// Send the vote information to the server. |
475
|
|
|
$.ajax({ |
476
|
|
|
type: "POST", |
477
|
|
|
url: opts.processVoteURL, |
478
|
|
|
data: d, |
479
|
|
|
error: function(request, textStatus, error) { |
|
|
|
|
480
|
|
|
showError('Oops, there was a problem casting that vote.'); |
481
|
|
|
} |
482
|
|
|
}); |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
/** |
486
|
|
|
* Open a reply form used to reply to an existing comment. |
487
|
|
|
*/ |
488
|
|
|
function openReply(id) { |
489
|
|
|
// Swap out the reply link for the hide link |
490
|
|
|
$('#rl' + id).hide(); |
491
|
|
|
$('#cr' + id).show(); |
492
|
|
|
|
493
|
|
|
// Add the reply li to the children ul. |
494
|
|
|
var div = $(renderTemplate(replyTemplate, {id: id})).hide(); |
495
|
|
|
$('#cl' + id) |
496
|
|
|
.prepend(div) |
497
|
|
|
// Setup the submit handler for the reply form. |
498
|
|
|
.find('#rf' + id) |
499
|
|
|
.submit(function(event) { |
500
|
|
|
event.preventDefault(); |
501
|
|
|
addComment($('#rf' + id)); |
502
|
|
|
closeReply(id); |
503
|
|
|
}) |
504
|
|
|
.find('input[type=button]') |
505
|
|
|
.click(function() { |
506
|
|
|
closeReply(id); |
507
|
|
|
}); |
508
|
|
|
div.slideDown('fast', function() { |
509
|
|
|
$('#rf' + id).find('textarea').focus(); |
510
|
|
|
}); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* Close the reply form opened with openReply. |
515
|
|
|
*/ |
516
|
|
|
function closeReply(id) { |
517
|
|
|
// Remove the reply div from the DOM. |
518
|
|
|
$('#rd' + id).slideUp('fast', function() { |
519
|
|
|
$(this).remove(); |
520
|
|
|
}); |
521
|
|
|
|
522
|
|
|
// Swap out the hide link for the reply link |
523
|
|
|
$('#cr' + id).hide(); |
524
|
|
|
$('#rl' + id).show(); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
/** |
528
|
|
|
* Recursively sort a tree of comments using the comp comparator. |
529
|
|
|
*/ |
530
|
|
|
function sortComments(comments) { |
531
|
|
|
comments.sort(comp); |
532
|
|
|
$.each(comments, function() { |
533
|
|
|
this.children = sortComments(this.children); |
534
|
|
|
}); |
535
|
|
|
return comments; |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* Get the children comments from a ul. If recursive is true, |
540
|
|
|
* recursively include childrens' children. |
541
|
|
|
*/ |
542
|
|
|
function getChildren(ul, recursive) { |
543
|
|
|
var children = []; |
544
|
|
|
ul.children().children("[id^='cd']") |
545
|
|
|
.each(function() { |
546
|
|
|
var comment = $(this).data('comment'); |
547
|
|
|
if (recursive) |
548
|
|
|
comment.children = getChildren($(this).find('#cl' + comment.id), true); |
|
|
|
|
549
|
|
|
children.push(comment); |
550
|
|
|
}); |
551
|
|
|
return children; |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
/** Create a div to display a comment in. */ |
555
|
|
|
function createCommentDiv(comment) { |
556
|
|
|
if (!comment.displayed && !opts.moderator) { |
557
|
|
|
return $('<div class="moderate">Thank you! Your comment will show up ' |
558
|
|
|
+ 'once it is has been approved by a moderator.</div>'); |
559
|
|
|
} |
560
|
|
|
// Prettify the comment rating. |
561
|
|
|
comment.pretty_rating = comment.rating + ' point' + |
562
|
|
|
(comment.rating == 1 ? '' : 's'); |
563
|
|
|
// Make a class (for displaying not yet moderated comments differently) |
564
|
|
|
comment.css_class = comment.displayed ? '' : ' moderate'; |
565
|
|
|
// Create a div for this comment. |
566
|
|
|
var context = $.extend({}, opts, comment); |
567
|
|
|
var div = $(renderTemplate(commentTemplate, context)); |
568
|
|
|
|
569
|
|
|
// If the user has voted on this comment, highlight the correct arrow. |
570
|
|
|
if (comment.vote) { |
571
|
|
|
var direction = (comment.vote == 1) ? 'u' : 'd'; |
572
|
|
|
div.find('#' + direction + 'v' + comment.id).hide(); |
573
|
|
|
div.find('#' + direction + 'u' + comment.id).show(); |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
if (opts.moderator || comment.text != '[deleted]') { |
577
|
|
|
div.find('a.reply').show(); |
578
|
|
|
if (comment.proposal_diff) |
579
|
|
|
div.find('#sp' + comment.id).show(); |
|
|
|
|
580
|
|
|
if (opts.moderator && !comment.displayed) |
581
|
|
|
div.find('#cm' + comment.id).show(); |
|
|
|
|
582
|
|
|
if (opts.moderator || (opts.username == comment.username)) |
583
|
|
|
div.find('#dc' + comment.id).show(); |
|
|
|
|
584
|
|
|
} |
585
|
|
|
return div; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
/** |
589
|
|
|
* A simple template renderer. Placeholders such as <%id%> are replaced |
590
|
|
|
* by context['id'] with items being escaped. Placeholders such as <#id#> |
591
|
|
|
* are not escaped. |
592
|
|
|
*/ |
593
|
|
|
function renderTemplate(template, context) { |
594
|
|
|
var esc = $(document.createElement('div')); |
595
|
|
|
|
596
|
|
|
function handle(ph, escape) { |
597
|
|
|
var cur = context; |
598
|
|
|
$.each(ph.split('.'), function() { |
599
|
|
|
cur = cur[this]; |
600
|
|
|
}); |
601
|
|
|
return escape ? esc.text(cur || "").html() : cur; |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
return template.replace(/<([%#])([\w\.]*)\1>/g, function() { |
605
|
|
|
return handle(arguments[2], arguments[1] == '%' ? true : false); |
606
|
|
|
}); |
607
|
|
|
} |
608
|
|
|
|
609
|
|
|
/** Flash an error message briefly. */ |
610
|
|
|
function showError(message) { |
611
|
|
|
$(document.createElement('div')).attr({'class': 'popup-error'}) |
612
|
|
|
.append($(document.createElement('div')) |
613
|
|
|
.attr({'class': 'error-message'}).text(message)) |
614
|
|
|
.appendTo('body') |
615
|
|
|
.fadeIn("slow") |
616
|
|
|
.delay(2000) |
617
|
|
|
.fadeOut("slow"); |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
/** Add a link the user uses to open the comments popup. */ |
621
|
|
|
$.fn.comment = function() { |
622
|
|
|
return this.each(function() { |
623
|
|
|
var id = $(this).attr('id').substring(1); |
624
|
|
|
var count = COMMENT_METADATA[id]; |
|
|
|
|
625
|
|
|
var title = count + ' comment' + (count == 1 ? '' : 's'); |
626
|
|
|
var image = count > 0 ? opts.commentBrightImage : opts.commentImage; |
627
|
|
|
var addcls = count == 0 ? ' nocomment' : ''; |
628
|
|
|
$(this) |
629
|
|
|
.append( |
630
|
|
|
$(document.createElement('a')).attr({ |
631
|
|
|
href: '#', |
632
|
|
|
'class': 'sphinx-comment-open' + addcls, |
633
|
|
|
id: 'ao' + id |
634
|
|
|
}) |
635
|
|
|
.append($(document.createElement('img')).attr({ |
636
|
|
|
src: image, |
637
|
|
|
alt: 'comment', |
638
|
|
|
title: title |
639
|
|
|
})) |
640
|
|
|
.click(function(event) { |
641
|
|
|
event.preventDefault(); |
642
|
|
|
show($(this).attr('id').substring(2)); |
643
|
|
|
}) |
644
|
|
|
) |
645
|
|
|
.append( |
646
|
|
|
$(document.createElement('a')).attr({ |
647
|
|
|
href: '#', |
648
|
|
|
'class': 'sphinx-comment-close hidden', |
649
|
|
|
id: 'ah' + id |
650
|
|
|
}) |
651
|
|
|
.append($(document.createElement('img')).attr({ |
652
|
|
|
src: opts.closeCommentImage, |
653
|
|
|
alt: 'close', |
654
|
|
|
title: 'close' |
655
|
|
|
})) |
656
|
|
|
.click(function(event) { |
657
|
|
|
event.preventDefault(); |
658
|
|
|
hide($(this).attr('id').substring(2)); |
659
|
|
|
}) |
660
|
|
|
); |
661
|
|
|
}); |
662
|
|
|
}; |
663
|
|
|
|
664
|
|
|
var opts = { |
665
|
|
|
processVoteURL: '/_process_vote', |
666
|
|
|
addCommentURL: '/_add_comment', |
667
|
|
|
getCommentsURL: '/_get_comments', |
668
|
|
|
acceptCommentURL: '/_accept_comment', |
669
|
|
|
deleteCommentURL: '/_delete_comment', |
670
|
|
|
commentImage: '/static/_static/comment.png', |
671
|
|
|
closeCommentImage: '/static/_static/comment-close.png', |
672
|
|
|
loadingImage: '/static/_static/ajax-loader.gif', |
673
|
|
|
commentBrightImage: '/static/_static/comment-bright.png', |
674
|
|
|
upArrow: '/static/_static/up.png', |
675
|
|
|
downArrow: '/static/_static/down.png', |
676
|
|
|
upArrowPressed: '/static/_static/up-pressed.png', |
677
|
|
|
downArrowPressed: '/static/_static/down-pressed.png', |
678
|
|
|
voting: false, |
679
|
|
|
moderator: false |
680
|
|
|
}; |
681
|
|
|
|
682
|
|
|
if (typeof COMMENT_OPTIONS != "undefined") { |
|
|
|
|
683
|
|
|
opts = jQuery.extend(opts, COMMENT_OPTIONS); |
684
|
|
|
} |
685
|
|
|
|
686
|
|
|
var popupTemplate = '\ |
687
|
|
|
<div class="sphinx-comments" id="sc<%id%>">\ |
688
|
|
|
<p class="sort-options">\ |
689
|
|
|
Sort by:\ |
690
|
|
|
<a href="#" class="sort-option byrating">best rated</a>\ |
691
|
|
|
<a href="#" class="sort-option byascage">newest</a>\ |
692
|
|
|
<a href="#" class="sort-option byage">oldest</a>\ |
693
|
|
|
</p>\ |
694
|
|
|
<div class="comment-header">Comments</div>\ |
695
|
|
|
<div class="comment-loading" id="cn<%id%>">\ |
696
|
|
|
loading comments... <img src="<%loadingImage%>" alt="" /></div>\ |
697
|
|
|
<ul id="cl<%id%>" class="comment-ul"></ul>\ |
698
|
|
|
<div id="ca<%id%>">\ |
699
|
|
|
<p class="add-a-comment">Add a comment\ |
700
|
|
|
(<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\ |
701
|
|
|
<div class="comment-markup-box" id="mb<%id%>">\ |
702
|
|
|
reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \ |
703
|
|
|
<code>``code``</code>, \ |
704
|
|
|
code blocks: <code>::</code> and an indented block after blank line</div>\ |
705
|
|
|
<form method="post" id="cf<%id%>" class="comment-form" action="">\ |
706
|
|
|
<textarea name="comment" cols="80"></textarea>\ |
707
|
|
|
<p class="propose-button">\ |
708
|
|
|
<a href="#" id="pc<%id%>" class="show-propose-change">\ |
709
|
|
|
Propose a change ▹\ |
710
|
|
|
</a>\ |
711
|
|
|
<a href="#" id="hc<%id%>" class="hide-propose-change">\ |
712
|
|
|
Propose a change ▿\ |
713
|
|
|
</a>\ |
714
|
|
|
</p>\ |
715
|
|
|
<textarea name="proposal" id="pt<%id%>" cols="80"\ |
716
|
|
|
spellcheck="false"></textarea>\ |
717
|
|
|
<input type="submit" value="Add comment" />\ |
718
|
|
|
<input type="hidden" name="node" value="<%id%>" />\ |
719
|
|
|
<input type="hidden" name="parent" value="" />\ |
720
|
|
|
</form>\ |
721
|
|
|
</div>\ |
722
|
|
|
</div>'; |
723
|
|
|
|
724
|
|
|
var commentTemplate = '\ |
725
|
|
|
<div id="cd<%id%>" class="sphinx-comment<%css_class%>">\ |
726
|
|
|
<div class="vote">\ |
727
|
|
|
<div class="arrow">\ |
728
|
|
|
<a href="#" id="uv<%id%>" class="vote" title="vote up">\ |
729
|
|
|
<img src="<%upArrow%>" />\ |
730
|
|
|
</a>\ |
731
|
|
|
<a href="#" id="uu<%id%>" class="un vote" title="vote up">\ |
732
|
|
|
<img src="<%upArrowPressed%>" />\ |
733
|
|
|
</a>\ |
734
|
|
|
</div>\ |
735
|
|
|
<div class="arrow">\ |
736
|
|
|
<a href="#" id="dv<%id%>" class="vote" title="vote down">\ |
737
|
|
|
<img src="<%downArrow%>" id="da<%id%>" />\ |
738
|
|
|
</a>\ |
739
|
|
|
<a href="#" id="du<%id%>" class="un vote" title="vote down">\ |
740
|
|
|
<img src="<%downArrowPressed%>" />\ |
741
|
|
|
</a>\ |
742
|
|
|
</div>\ |
743
|
|
|
</div>\ |
744
|
|
|
<div class="comment-content">\ |
745
|
|
|
<p class="tagline comment">\ |
746
|
|
|
<span class="user-id"><%username%></span>\ |
747
|
|
|
<span class="rating"><%pretty_rating%></span>\ |
748
|
|
|
<span class="delta"><%time.delta%></span>\ |
749
|
|
|
</p>\ |
750
|
|
|
<div class="comment-text comment"><#text#></div>\ |
751
|
|
|
<p class="comment-opts comment">\ |
752
|
|
|
<a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\ |
753
|
|
|
<a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\ |
754
|
|
|
<a href="#" id="sp<%id%>" class="show-proposal">proposal ▹</a>\ |
755
|
|
|
<a href="#" id="hp<%id%>" class="hide-proposal">proposal ▿</a>\ |
756
|
|
|
<a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\ |
757
|
|
|
<span id="cm<%id%>" class="moderation hidden">\ |
758
|
|
|
<a href="#" id="ac<%id%>" class="accept-comment">accept</a>\ |
759
|
|
|
</span>\ |
760
|
|
|
</p>\ |
761
|
|
|
<pre class="proposal" id="pr<%id%>">\ |
762
|
|
|
<#proposal_diff#>\ |
763
|
|
|
</pre>\ |
764
|
|
|
<ul class="comment-children" id="cl<%id%>"></ul>\ |
765
|
|
|
</div>\ |
766
|
|
|
<div class="clearleft"></div>\ |
767
|
|
|
</div>\ |
768
|
|
|
</div>'; |
769
|
|
|
|
770
|
|
|
var replyTemplate = '\ |
771
|
|
|
<li>\ |
772
|
|
|
<div class="reply-div" id="rd<%id%>">\ |
773
|
|
|
<form id="rf<%id%>">\ |
774
|
|
|
<textarea name="comment" cols="80"></textarea>\ |
775
|
|
|
<input type="submit" value="Add reply" />\ |
776
|
|
|
<input type="button" value="Cancel" />\ |
777
|
|
|
<input type="hidden" name="parent" value="<%id%>" />\ |
778
|
|
|
<input type="hidden" name="node" value="" />\ |
779
|
|
|
</form>\ |
780
|
|
|
</div>\ |
781
|
|
|
</li>'; |
782
|
|
|
|
783
|
|
|
$(document).ready(function() { |
784
|
|
|
init(); |
785
|
|
|
}); |
786
|
|
|
})(jQuery); |
787
|
|
|
|
788
|
|
|
$(document).ready(function() { |
789
|
|
|
// add comment anchors for all paragraphs that are commentable |
790
|
|
|
$('.sphinx-has-comment').comment(); |
791
|
|
|
|
792
|
|
|
// highlight search words in search results |
793
|
|
|
$("div.context").each(function() { |
794
|
|
|
var params = $.getQueryParameters(); |
795
|
|
|
var terms = (params.q) ? params.q[0].split(/\s+/) : []; |
796
|
|
|
var result = $(this); |
797
|
|
|
$.each(terms, function() { |
798
|
|
|
result.highlightText(this.toLowerCase(), 'highlighted'); |
799
|
|
|
}); |
800
|
|
|
}); |
801
|
|
|
|
802
|
|
|
// directly open comment window if requested |
803
|
|
|
var anchor = document.location.hash; |
804
|
|
|
if (anchor.substring(0, 9) == '#comment-') { |
805
|
|
|
$('#ao' + anchor.substring(9)).click(); |
806
|
|
|
document.location.hash = '#s' + anchor.substring(9); |
807
|
|
|
} |
808
|
|
|
}); |
809
|
|
|
|
This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.