|
1 /* |
|
2 * websupport.js |
|
3 * ~~~~~~~~~~~~~ |
|
4 * |
|
5 * sphinx.websupport utilties for all documentation. |
|
6 * |
|
7 * :copyright: Copyright 2007-2011 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 $('a.comment-close').live("click", function(event) { |
|
54 event.preventDefault(); |
|
55 hide($(this).attr('id').substring(2)); |
|
56 }); |
|
57 $('a.vote').live("click", function(event) { |
|
58 event.preventDefault(); |
|
59 handleVote($(this)); |
|
60 }); |
|
61 $('a.reply').live("click", function(event) { |
|
62 event.preventDefault(); |
|
63 openReply($(this).attr('id').substring(2)); |
|
64 }); |
|
65 $('a.close-reply').live("click", function(event) { |
|
66 event.preventDefault(); |
|
67 closeReply($(this).attr('id').substring(2)); |
|
68 }); |
|
69 $('a.sort-option').live("click", function(event) { |
|
70 event.preventDefault(); |
|
71 handleReSort($(this)); |
|
72 }); |
|
73 $('a.show-proposal').live("click", function(event) { |
|
74 event.preventDefault(); |
|
75 showProposal($(this).attr('id').substring(2)); |
|
76 }); |
|
77 $('a.hide-proposal').live("click", function(event) { |
|
78 event.preventDefault(); |
|
79 hideProposal($(this).attr('id').substring(2)); |
|
80 }); |
|
81 $('a.show-propose-change').live("click", function(event) { |
|
82 event.preventDefault(); |
|
83 showProposeChange($(this).attr('id').substring(2)); |
|
84 }); |
|
85 $('a.hide-propose-change').live("click", function(event) { |
|
86 event.preventDefault(); |
|
87 hideProposeChange($(this).attr('id').substring(2)); |
|
88 }); |
|
89 $('a.accept-comment').live("click", function(event) { |
|
90 event.preventDefault(); |
|
91 acceptComment($(this).attr('id').substring(2)); |
|
92 }); |
|
93 $('a.delete-comment').live("click", function(event) { |
|
94 event.preventDefault(); |
|
95 deleteComment($(this).attr('id').substring(2)); |
|
96 }); |
|
97 $('a.comment-markup').live("click", 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 <tt>``code``</tt>, \ |
|
704 code blocks: <tt>::</tt> 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 }); |