13 $.fn.autogrow =
function() {
14 return this.each(
function() {
17 $.fn.autogrow.resize(textarea);
21 textarea.interval = setInterval(
function() {
22 $.fn.autogrow.resize(textarea);
26 clearInterval(textarea.interval);
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;
36 $.each(lines,
function() {
37 lineCount += Math.ceil(this.length / columns) || 1;
39 var height = lineHeight * (lineCount + 1);
40 $(textarea).css(
'height', height);
52 function initEvents() {
53 $(document).on(
"click",
'a.comment-close',
function(event) {
54 event.preventDefault();
55 hide($(
this).attr(
'id').substring(2));
57 $(document).on(
"click",
'a.vote',
function(event) {
58 event.preventDefault();
61 $(document).on(
"click",
'a.reply',
function(event) {
62 event.preventDefault();
63 openReply($(
this).attr(
'id').substring(2));
65 $(document).on(
"click",
'a.close-reply',
function(event) {
66 event.preventDefault();
67 closeReply($(
this).attr(
'id').substring(2));
69 $(document).on(
"click",
'a.sort-option',
function(event) {
70 event.preventDefault();
71 handleReSort($(
this));
73 $(document).on(
"click",
'a.show-proposal',
function(event) {
74 event.preventDefault();
75 showProposal($(
this).attr(
'id').substring(2));
77 $(document).on(
"click",
'a.hide-proposal',
function(event) {
78 event.preventDefault();
79 hideProposal($(
this).attr(
'id').substring(2));
81 $(document).on(
"click",
'a.show-propose-change',
function(event) {
82 event.preventDefault();
83 showProposeChange($(
this).attr(
'id').substring(2));
85 $(document).on(
"click",
'a.hide-propose-change',
function(event) {
86 event.preventDefault();
87 hideProposeChange($(
this).attr(
'id').substring(2));
89 $(document).on(
"click",
'a.accept-comment',
function(event) {
90 event.preventDefault();
91 acceptComment($(
this).attr(
'id').substring(2));
93 $(document).on(
"click",
'a.delete-comment',
function(event) {
94 event.preventDefault();
95 deleteComment($(
this).attr(
'id').substring(2));
97 $(document).on(
"click",
'a.comment-markup',
function(event) {
98 event.preventDefault();
99 toggleCommentMarkupBox($(
this).attr(
'id').substring(2));
107 function setComparator() {
110 if (by.substring(0,3) ==
'asc') {
111 var i = by.substring(3);
112 comp =
function(
a, b) {
return a[i] - b[i]; };
115 comp =
function(
a, b) {
return b[by] -
a[by]; };
119 $(
'a.sel').attr(
'href',
'#').removeClass(
'sel');
120 $(
'a.by' + by).removeAttr(
'href').addClass(
'sel');
127 function initComparator() {
130 if (document.cookie.length > 0) {
131 var start = document.cookie.indexOf(
'sortBy=');
134 var end = document.cookie.indexOf(
";", start);
136 end = document.cookie.length;
137 by = unescape(document.cookie.substring(start, end));
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();
159 $(
'#s' + id).after(popup);
160 popup.slideDown(
'fast',
function() {
169 $(
'#ah' + id).hide();
170 $(
'#ao' + id).show();
171 var div = $(
'#sc' + id);
172 div.slideUp(
'fast',
function() {
181 function getComments(
id) {
184 url: opts.getCommentsURL,
186 success:
function(data, textStatus, request) {
187 var ul = $(
'#cl' + id);
190 .find(
'textarea[name="proposal"]')
191 .data(
'source', data.source);
193 if (data.comments.length === 0) {
194 ul.html(
'<li>No comments yet.</li>');
195 ul.data(
'empty',
true);
198 var comments = sortComments(data.comments);
199 speed = data.comments.length * 100;
200 appendComments(comments, ul);
201 ul.data(
'empty',
false);
203 $(
'#cn' + id).slideUp(speed + 200);
206 error:
function(request, textStatus, error) {
207 showError(
'Oops, there was a problem retrieving the comments.');
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();
223 showError(
'Please enter a comment.');
228 form.find(
'textarea,input').attr(
'disabled',
'disabled');
233 url: opts.addCommentURL,
241 success:
function(data, textStatus, error) {
244 hideProposeChange(node_id);
246 form.find(
'textarea')
248 .add(form.find(
'input'))
249 .removeAttr(
'disabled');
250 var ul = $(
'#cl' + (node_id || parent_id));
251 if (ul.data(
'empty')) {
253 ul.data(
'empty',
false);
255 insertComment(data.comment);
256 var ao = $(
'#ao' + node_id);
257 ao.find(
'img').attr({
'src': opts.commentBrightImage});
261 $(
'#ca' + node_id).slideUp();
264 error:
function(request, textStatus, error) {
265 form.find(
'textarea,input').removeAttr(
'disabled');
266 showError(
'Oops, there was a problem adding the comment.');
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'));
281 this.children = null;
282 div.data(
'comment',
this);
290 function insertComment(comment) {
291 var div = createCommentDiv(comment);
294 comment.children = null;
295 div.data(
'comment', comment);
297 var ul = $(
'#cl' + (comment.node || comment.parent));
298 var siblings = getChildren(ul);
300 var li = $(document.createElement(
'li'));
304 for(i=0; i < siblings.length; i++) {
305 if (comp(comment, siblings[i]) <= 0) {
306 $(
'#cd' + siblings[i].id)
308 .before(li.html(div));
309 li.slideDown(
'fast');
316 ul.append(li.html(div));
317 li.slideDown(
'fast');
320 function acceptComment(
id) {
323 url: opts.acceptCommentURL,
325 success:
function(data, textStatus, request) {
326 $(
'#cm' + id).fadeOut(
'fast');
327 $(
'#cd' + id).removeClass(
'moderate');
329 error:
function(request, textStatus, error) {
330 showError(
'Oops, there was a problem accepting the comment.');
335 function deleteComment(
id) {
338 url: opts.deleteCommentURL,
340 success:
function(data, textStatus, request) {
341 var div = $(
'#cd' + id);
342 if (data ==
'delete') {
344 div.slideUp(
'fast',
function() {
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)
358 var comment = div.data(
'comment');
359 comment.username =
'[deleted]';
360 comment.text =
'[deleted]';
361 div.data(
'comment', comment);
363 error:
function(request, textStatus, error) {
364 showError(
'Oops, there was a problem deleting the comment.');
369 function showProposal(
id) {
370 $(
'#sp' + id).hide();
371 $(
'#hp' + id).show();
372 $(
'#pr' + id).slideDown(
'fast');
375 function hideProposal(
id) {
376 $(
'#hp' + id).hide();
377 $(
'#sp' + id).show();
378 $(
'#pr' + id).slideUp(
'fast');
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');
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');
398 function toggleCommentMarkupBox(
id) {
399 $(
'#mb' + id).toggle();
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);
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());
426 function handleVote(link) {
428 showError(
"You'll need to login to vote.");
432 var
id = link.attr(
'id');
440 if (
id.charAt(1) !=
'u') {
441 value =
id.charAt(0) ==
'u' ? 1 : -1;
445 comment_id:
id.substring(2),
451 $(
'#' +
id.charAt(0) + (
id.charAt(1) ==
'u' ?
'v' :
'u') + d.comment_id)
455 var div = $(
'div#cd' + d.comment_id);
456 var data = div.data(
'comment');
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();
466 data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
468 div.data(
'comment', data);
471 div.find(
'.rating:first')
472 .text(data.rating +
' point' + (data.rating == 1 ?
'' :
's'));
477 url: opts.processVoteURL,
479 error:
function(request, textStatus, error) {
480 showError(
'Oops, there was a problem casting that vote.');
488 function openReply(
id) {
490 $(
'#rl' + id).hide();
491 $(
'#cr' + id).show();
494 var div = $(renderTemplate(replyTemplate, {
id:
id})).hide();
499 .submit(
function(event) {
500 event.preventDefault();
501 addComment($(
'#rf' +
id));
504 .find(
'input[type=button]')
508 div.slideDown(
'fast',
function() {
509 $(
'#rf' + id).find(
'textarea').focus();
516 function closeReply(
id) {
518 $(
'#rd' + id).slideUp(
'fast',
function() {
523 $(
'#cr' + id).hide();
524 $(
'#rl' + id).show();
530 function sortComments(comments) {
532 $.each(comments,
function() {
533 this.children = sortComments(this.children);
542 function getChildren(ul, recursive) {
544 ul.children().children(
"[id^='cd']")
546 var comment = $(
this).data(
'comment');
548 comment.children = getChildren($(
this).find(
'#cl' + comment.id),
true);
549 children.push(comment);
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>');
561 comment.pretty_rating = comment.rating +
' point' +
562 (comment.rating == 1 ?
'' :
's');
564 comment.css_class = comment.displayed ?
'' :
' moderate';
566 var context = $.extend({}, opts, comment);
567 var div = $(renderTemplate(commentTemplate, context));
571 var direction = (comment.vote == 1) ?
'u' :
'd';
572 div.find(
'#' + direction +
'v' + comment.id).hide();
573 div.find(
'#' + direction +
'u' + comment.id).show();
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();
593 function renderTemplate(
template, context) {
594 var esc = $(document.createElement(
'div'));
596 function handle(ph, escape) {
598 $.each(ph.split(
'.'),
function() {
601 return escape ? esc.text(cur ||
"").html() : cur;
604 return template.replace(/<([%#])([\w\.]*)\1>/g,
function() {
605 return handle(arguments[2], arguments[1] ==
'%' ?
true :
false);
610 function showError(message) {
611 $(document.createElement(
'div')).attr({
'class':
'popup-error'})
612 .append($(document.createElement(
'div'))
613 .attr({
'class':
'error-message'}).text(message))
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' :
'';
630 $(document.createElement(
'a')).attr({
632 'class':
'sphinx-comment-open' + addcls,
635 .append($(document.createElement(
'img')).attr({
640 .click(
function(event) {
641 event.preventDefault();
642 show($(
this).attr(
'id').substring(2));
646 $(document.createElement(
'a')).attr({
648 'class':
'sphinx-comment-close hidden',
651 .append($(document.createElement(
'img')).attr({
652 src: opts.closeCommentImage,
656 .click(
function(event) {
657 event.preventDefault();
658 hide($(
this).attr(
'id').substring(2));
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',
682 if (typeof COMMENT_OPTIONS !=
"undefined") {
683 opts = jQuery.extend(opts, COMMENT_OPTIONS);
686 var popupTemplate =
'\ 687 <div class="sphinx-comments" id="sc<%id%>">\ 688 <p class="sort-options">\ 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>\ 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>\ 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 ▹\ 711 <a href="#" id="hc<%id%>" class="hide-propose-change">\ 712 Propose a change ▿\ 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="" />\ 724 var commentTemplate =
'\ 725 <div id="cd<%id%>" class="sphinx-comment<%css_class%>">\ 728 <a href="#" id="uv<%id%>" class="vote" title="vote up">\ 729 <img src="<%upArrow%>" />\ 731 <a href="#" id="uu<%id%>" class="un vote" title="vote up">\ 732 <img src="<%upArrowPressed%>" />\ 736 <a href="#" id="dv<%id%>" class="vote" title="vote down">\ 737 <img src="<%downArrow%>" id="da<%id%>" />\ 739 <a href="#" id="du<%id%>" class="un vote" title="vote down">\ 740 <img src="<%downArrowPressed%>" />\ 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>\ 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>\ 761 <pre class="proposal" id="pr<%id%>">\ 764 <ul class="comment-children" id="cl<%id%>"></ul>\ 766 <div class="clearleft"></div>\ 770 var replyTemplate =
'\ 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="" />\ 783 $(document).ready(
function() {
788 $(document).ready(
function() {
790 $(
'.sphinx-has-comment').comment();
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');
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);