display comments inline

This commit is contained in:
Jacob Mason 2010-08-18 15:41:12 -05:00
parent fd10895e5a
commit 363c2f702e
3 changed files with 187 additions and 192 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -18,7 +18,7 @@
}; };
$.fn.autogrow.resize = function(textarea) { $.fn.autogrow.resize = function(textarea) {
var lineHeight = parseInt($(textarea).css('line-height')); var lineHeight = parseInt($(textarea).css('line-height'), 10);
var lines = textarea.value.split('\n'); var lines = textarea.value.split('\n');
var columns = textarea.cols; var columns = textarea.cols;
var lineCount = 0; var lineCount = 0;
@ -31,22 +31,17 @@
})(jQuery); })(jQuery);
(function($) { (function($) {
var commentListEmpty, popup, comp; var comp;
function init() { function init() {
initTemplates();
initEvents(); initEvents();
initComparator(); initComparator();
}; }
function initEvents() { function initEvents() {
$('a#comment_close').click(function(event) { $('a.comment_close').live("click", function(event) {
event.preventDefault(); hide($(this).attr('id').substring(2));
hide(); return false;
});
$('form#comment_form').submit(function(event) {
event.preventDefault();
addComment($('form#comment_form'));
}); });
$('.vote').live("click", function() { $('.vote').live("click", function() {
handleVote($(this)); handleVote($(this));
@ -60,9 +55,9 @@
closeReply($(this).attr('id').substring(2)); closeReply($(this).attr('id').substring(2));
return false; return false;
}); });
$('a.sort_option').click(function(event) { $('a.sort_option').live("click", function(event) {
event.preventDefault();
handleReSort($(this)); handleReSort($(this));
return false;
}); });
$('a.show_proposal').live("click", function() { $('a.show_proposal').live("click", function() {
showProposal($(this).attr('id').substring(2)); showProposal($(this).attr('id').substring(2));
@ -92,16 +87,27 @@
deleteComment($(this).attr('id').substring(2)); deleteComment($(this).attr('id').substring(2));
return false; return false;
}); });
}; }
function initTemplates() { /*
// Create our popup div, the same div is recycled each time comments Set comp, which is a comparator function used for sorting and
// are displayed. inserting comments into the list.
popup = $(renderTemplate(popupTemplate, opts)); */
// Setup autogrow on the textareas function setComparator(by) {
popup.find('textarea').autogrow(); // If the first three letters are "asc", sort in ascending order
$('body').append(popup); // and remove the prefix.
}; if (by.substring(0,3) == 'asc') {
var i = by.substring(3);
comp = function(a, b) { return a[i] - b[i]; };
} else {
// Otherwise sort in descending order.
comp = function(a, b) { return b[by] - a[by]; };
}
// Reset link styles and format the selected sort option.
$('a.sel').attr('href', '#').removeClass('sel');
$('#' + by).removeAttr('href').addClass('sel');
}
/* /*
Create a comp function. If the user has preferences stored in Create a comp function. If the user has preferences stored in
@ -115,59 +121,46 @@
if (start != -1) { if (start != -1) {
start = start + 7; start = start + 7;
var end = document.cookie.indexOf(";", start); var end = document.cookie.indexOf(";", start);
if (end == -1) if (end == -1) {
end = document.cookie.length; end = document.cookie.length;
by = unescape(document.cookie.substring(start, end)); by = unescape(document.cookie.substring(start, end));
} }
} }
}
setComparator(by); setComparator(by);
}; }
/* /*
Show the comments popup window. Show a comment div.
*/ */
function show(nodeId) { function show(id) {
var id = nodeId.substring(1); $('#ao' + id).hide();
$('#ah' + id).show();
// Reset the main comment form, and set the value of the parent input. var context = $.extend({id: id}, opts);
$('form#comment_form') var popup = $(renderTemplate(popupTemplate, context)).hide();
.find('textarea,input') popup.find('textarea[name="proposal"]').hide();
.removeAttr('disabled').end() var form = popup.find('#cf' + id);
.find('input[name="node"]') form.submit(function(event) {
.val(id).end() event.preventDefault();
.find('textarea[name="proposal"]') addComment(form);
.val('') });
.hide(); $('#s' + id).after(popup);
popup.slideDown('fast', function() {
// Position the popup and show it.
var clientWidth = document.documentElement.clientWidth;
var popupWidth = $('div.popup_comment').width();
$('div#focuser').fadeIn('fast');
$('div.popup_comment')
.css({
'top': 100 + $(window).scrollTop(),
'left': clientWidth / 2 - popupWidth / 2,
'position': 'absolute'
})
.fadeIn('fast', function() {
getComments(id); getComments(id);
}); });
}; }
/* /*
Hide the comments popup window. Hide a comment div.
*/ */
function hide() { function hide(id) {
$('div#focuser').fadeOut('fast'); $('#ah' + id).hide();
$('div.popup_comment').fadeOut('fast', function() { $('#ao' + id).show();
$('ul#comment_ul').empty(); var div = $('#sc' + id);
$('h3#comment_notification').show(); div.slideUp('fast', function() {
$('form#comment_form').find('textarea') div.remove();
.val('').end()
.find('textarea, input')
.removeAttr('disabled');
}); });
}; }
/* /*
Perform an ajax request to get comments for a node Perform an ajax request to get comments for a node
@ -179,23 +172,23 @@
url: opts.getCommentsURL, url: opts.getCommentsURL,
data: {node: id}, data: {node: id},
success: function(data, textStatus, request) { success: function(data, textStatus, request) {
var ul = $('ul#comment_ul').hide(); var ul = $('#cl' + id);
$('form#comment_form') var speed = 100;
$('#cf' + id)
.find('textarea[name="proposal"]') .find('textarea[name="proposal"]')
.data('source', data.source); .data('source', data.source);
if (data.comments.length == 0) { if (data.comments.length === 0) {
ul.html('<li>No comments yet.</li>'); ul.html('<li>No comments yet.</li>');
commentListEmpty = true; ul.data('empty', true);
var speed = 100;
} else { } else {
// If there are comments, sort them and put them in the list. // If there are comments, sort them and put them in the list.
var comments = sortComments(data.comments); var comments = sortComments(data.comments);
var speed = data.comments.length * 100; speed = data.comments.length * 100;
appendComments(comments, ul); appendComments(comments, ul);
commentListEmpty = false; ul.data('empty', false);
} }
$('h3#comment_notification').slideUp(speed + 200); $('#cn' + id).slideUp(speed + 200);
ul.slideDown(speed); ul.slideDown(speed);
}, },
error: function(request, textStatus, error) { error: function(request, textStatus, error) {
@ -203,7 +196,7 @@
}, },
dataType: 'json' dataType: 'json'
}); });
}; }
/* /*
Add a comment via ajax and insert the comment into the comment tree. Add a comment via ajax and insert the comment into the comment tree.
@ -212,6 +205,7 @@
// Disable the form that is being submitted. // Disable the form that is being submitted.
form.find('textarea,input').attr('disabled', 'disabled'); form.find('textarea,input').attr('disabled', 'disabled');
var node_id = form.find('input[name="node"]').val(); var node_id = form.find('input[name="node"]').val();
var parent_id = form.find('input[name="parent"]').val();
// Send the comment to the server. // Send the comment to the server.
$.ajax({ $.ajax({
@ -220,7 +214,7 @@
dataType: 'json', dataType: 'json',
data: { data: {
node: node_id, node: node_id,
parent: form.find('input[name="parent"]').val(), parent: parent_id,
text: form.find('textarea[name="comment"]').val(), text: form.find('textarea[name="comment"]').val(),
proposal: form.find('textarea[name="proposal"]').val() proposal: form.find('textarea[name="proposal"]').val()
}, },
@ -233,9 +227,10 @@
.val('') .val('')
.add(form.find('input')) .add(form.find('input'))
.removeAttr('disabled'); .removeAttr('disabled');
if (commentListEmpty) { var ul = $('#cl' + (node_id || parent_id));
$('ul#comment_ul').empty(); if (ul.data('empty')) {
commentListEmpty = false; $(ul).empty();
ul.data('empty', false);
} }
insertComment(data.comment); insertComment(data.comment);
}, },
@ -244,7 +239,7 @@
showError('Oops, there was a problem adding the comment.'); showError('Oops, there was a problem adding the comment.');
} }
}); });
}; }
/* /*
Recursively append comments to the main comment list and children Recursively append comments to the main comment list and children
@ -259,7 +254,7 @@
this.children = null; this.children = null;
div.data('comment', this); div.data('comment', this);
}); });
}; }
/* /*
After adding a new comment, it must be inserted in the correct After adding a new comment, it must be inserted in the correct
@ -272,13 +267,8 @@
comment.children = null; comment.children = null;
div.data('comment', comment); div.data('comment', comment);
if (comment.node != null) { var ul = $('#cl' + (comment.node || comment.parent));
var ul = $('ul#comment_ul');
var siblings = getChildren(ul); var siblings = getChildren(ul);
} else {
var ul = $('#cl' + comment.parent);
var siblings = getChildren(ul);
}
var li = $(document.createElement('li')); var li = $(document.createElement('li'));
li.hide(); li.hide();
@ -298,7 +288,7 @@
// or it is the only comment in the list. // or it is the only comment in the list.
ul.append(li.html(div)); ul.append(li.html(div));
li.slideDown('fast'); li.slideDown('fast');
}; }
function acceptComment(id) { function acceptComment(id) {
$.ajax({ $.ajax({
@ -310,9 +300,9 @@
}, },
error: function(request, textStatus, error) { error: function(request, textStatus, error) {
showError("Oops, there was a problem accepting the comment."); showError("Oops, there was a problem accepting the comment.");
}, }
}); });
}; }
function rejectComment(id) { function rejectComment(id) {
$.ajax({ $.ajax({
@ -327,9 +317,9 @@
}, },
error: function(request, textStatus, error) { error: function(request, textStatus, error) {
showError("Oops, there was a problem rejecting the comment."); showError("Oops, there was a problem rejecting the comment.");
}, }
}); });
}; }
function deleteComment(id) { function deleteComment(id) {
$.ajax({ $.ajax({
@ -353,54 +343,62 @@
}, },
error: function(request, textStatus, error) { error: function(request, textStatus, error) {
showError("Oops, there was a problem deleting the comment."); showError("Oops, there was a problem deleting the comment.");
}, }
}); });
}; }
function showProposal(id) { function showProposal(id) {
$('#sp' + id).hide(); $('#sp' + id).hide();
$('#hp' + id).show(); $('#hp' + id).show();
$('#pr' + id).slideDown('fast'); $('#pr' + id).slideDown('fast');
}; }
function hideProposal(id) { function hideProposal(id) {
$('#hp' + id).hide(); $('#hp' + id).hide();
$('#sp' + id).show(); $('#sp' + id).show();
$('#pr' + id).slideUp('fast'); $('#pr' + id).slideUp('fast');
}; }
function showProposeChange(id) { function showProposeChange(id) {
$('a.show_propose_change').hide(); $('#pc' + id).hide();
$('a.hide_propose_change').show(); $('#hc' + id).show();
var textarea = $('textarea[name="proposal"]'); var textarea = $('#pt' + id);
textarea.val(textarea.data('source')); textarea.val(textarea.data('source'));
$.fn.autogrow.resize(textarea[0]); $.fn.autogrow.resize(textarea[0]);
textarea.slideDown('fast'); textarea.slideDown('fast');
}; }
function hideProposeChange(id) { function hideProposeChange(id) {
$('a.hide_propose_change').hide(); $('#hc' + id).hide();
$('a.show_propose_change').show(); $('#pc' + id).show();
var textarea = $('textarea[name="proposal"]'); var textarea = $('#pt' + id);
textarea.val('').removeAttr('disabled'); textarea.val('').removeAttr('disabled');
textarea.slideUp('fast'); textarea.slideUp('fast');
}; }
/* /*
Handle when the user clicks on a sort by link. Handle when the user clicks on a sort by link.
*/ */
function handleReSort(link) { function handleReSort(link) {
setComparator(link.attr('id')); var classes = link.attr('class').split(/\s+/);
var by = '';
for (var i=0; i<classes.length; i++) {
if (classes[i] != 'sort_option') {
by = classes[i];
}
}
setComparator(by);
// Save/update the sortBy cookie. // Save/update the sortBy cookie.
var expiration = new Date(); var expiration = new Date();
expiration.setDate(expiration.getDate() + 365); expiration.setDate(expiration.getDate() + 365);
document.cookie= 'sortBy=' + escape(link.attr('id')) + document.cookie= 'sortBy=' + escape(by) +
';expires=' + expiration.toUTCString(); ';expires=' + expiration.toUTCString();
var comments = getChildren($('ul#comment_ul'), true); $('ul.comment_ul').each(function(index, ul) {
var comments = getChildren($(ul), true);
comments = sortComments(comments); comments = sortComments(comments);
appendComments(comments, $(ul).empty());
appendComments(comments, $('ul#comment_ul').empty()); });
}; }
/* /*
Function to process a vote when a user clicks an arrow. Function to process a vote when a user clicks an arrow.
@ -414,10 +412,9 @@
var id = link.attr('id'); var id = link.attr('id');
// If it is an unvote, the new vote value is 0, // If it is an unvote, the new vote value is 0,
// Otherwise it's 1 for an upvote, or -1 for a downvote. // Otherwise it's 1 for an upvote, or -1 for a downvote.
if (id.charAt(1) == 'u') {
var value = 0; var value = 0;
} else { if (id.charAt(1) != 'u') {
var value = id.charAt(0) == 'u' ? 1 : -1; value = id.charAt(0) == 'u' ? 1 : -1;
} }
// The data to be sent to the server. // The data to be sent to the server.
var d = { var d = {
@ -436,13 +433,13 @@
// If this is not an unvote, and the other vote arrow has // If this is not an unvote, and the other vote arrow has
// already been pressed, unpress it. // already been pressed, unpress it.
if ((d.value != 0) && (data.vote == d.value * -1)) { if ((d.value !== 0) && (data.vote === d.value * -1)) {
$('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide(); $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide();
$('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show(); $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show();
} }
// Update the comments rating in the local data. // Update the comments rating in the local data.
data.rating += (data.vote == 0) ? d.value : (d.value - data.vote); data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
data.vote = d.value; data.vote = d.value;
div.data('comment', data); div.data('comment', data);
@ -459,7 +456,7 @@
showError("Oops, there was a problem casting that vote."); showError("Oops, there was a problem casting that vote.");
} }
}); });
}; }
/* /*
Open a reply form used to reply to an existing comment. Open a reply form used to reply to an existing comment.
@ -481,7 +478,7 @@
closeReply(id); closeReply(id);
}); });
div.slideDown('fast'); div.slideDown('fast');
}; }
/* /*
Close the reply form opened with openReply. Close the reply form opened with openReply.
@ -495,7 +492,7 @@
// Swap out the hide link for the reply link // Swap out the hide link for the reply link
$('#cr' + id).hide(); $('#cr' + id).hide();
$('#rl' + id).show(); $('#rl' + id).show();
}; }
/* /*
Recursively sort a tree of comments using the comp comparator. Recursively sort a tree of comments using the comp comparator.
@ -506,28 +503,8 @@
this.children = sortComments(this.children); this.children = sortComments(this.children);
}); });
return comments; return comments;
};
/*
Set comp, which is a comparator function used for sorting and
inserting comments into the list.
*/
function setComparator(by) {
// If the first three letters are "asc", sort in ascending order
// and remove the prefix.
if (by.substring(0,3) == 'asc') {
var i = by.substring(3);
comp = function(a, b) { return a[i] - b[i]; }
} else {
// Otherwise sort in descending order.
comp = function(a, b) { return b[by] - a[by]; }
} }
// Reset link styles and format the selected sort option.
$('a.sel').attr('href', '#').removeClass('sel');
$('#' + by).removeAttr('href').addClass('sel');
};
/* /*
Get the children comments from a ul. If recursive is true, Get the children comments from a ul. If recursive is true,
recursively include childrens' children. recursively include childrens' children.
@ -543,7 +520,7 @@
children.push(comment); children.push(comment);
}); });
return children; return children;
}; }
/* /*
Create a div to display a comment in. Create a div to display a comment in.
@ -580,7 +557,8 @@
/* /*
A simple template renderer. Placeholders such as <%id%> are replaced A simple template renderer. Placeholders such as <%id%> are replaced
by context['id']. Items are always escaped. by context['id'] with items being escaped. Placeholders such as <#id#>
are not escaped.
*/ */
function renderTemplate(template, context) { function renderTemplate(template, context) {
var esc = $(document.createElement('div')); var esc = $(document.createElement('div'));
@ -596,16 +574,16 @@
return template.replace(/<([%#])([\w\.]*)\1>/g, function(){ return template.replace(/<([%#])([\w\.]*)\1>/g, function(){
return handle(arguments[2], arguments[1] == '%' ? true : false); return handle(arguments[2], arguments[1] == '%' ? true : false);
}); });
}; }
function showError(message) { function showError(message) {
$(document.createElement('div')).attr({class: 'popup_error'}) $(document.createElement('div')).attr({'class': 'popup_error'})
.append($(document.createElement('h1')).text(message)) .append($(document.createElement('h1')).text(message))
.appendTo('body') .appendTo('body')
.fadeIn("slow") .fadeIn("slow")
.delay(2000) .delay(2000)
.fadeOut("slow"); .fadeOut("slow");
}; }
/* /*
Add a link the user uses to open the comments popup. Add a link the user uses to open the comments popup.
@ -613,11 +591,16 @@
$.fn.comment = function() { $.fn.comment = function() {
return this.each(function() { return this.each(function() {
var id = $(this).attr('id').substring(1); var id = $(this).attr('id').substring(1);
var count = COMMENT_METADATA[id] var count = COMMENT_METADATA[id];
var title = count + ' comment' + (count == 1 ? '' : 's'); var title = count + ' comment' + (count == 1 ? '' : 's');
var image = count > 0 ? opts.commentBrightImage : opts.commentImage; var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
$(this).append( $(this)
$(document.createElement('a')).attr({href: '#', class: 'sphinx_comment'}) .append(
$(document.createElement('a')).attr({
href: '#',
'class': 'sphinx_comment',
id: 'ao' + id
})
.append($(document.createElement('img')).attr({ .append($(document.createElement('img')).attr({
src: image, src: image,
alt: 'comment', alt: 'comment',
@ -625,7 +608,23 @@
})) }))
.click(function(event) { .click(function(event) {
event.preventDefault(); event.preventDefault();
show($(this).parent().attr('id')); show($(this).attr('id').substring(2));
})
)
.append(
$(document.createElement('a')).attr({
href: '#',
'class': 'sphinx_comment_close hidden',
id: 'ah' + id
})
.append($(document.createElement('img')).attr({
src: opts.closeCommentImage,
alt: 'close',
title: 'close'
}))
.click(function(event) {
event.preventDefault();
hide($(this).attr('id').substring(2));
}) })
); );
}); });
@ -637,8 +636,9 @@
getCommentsURL: '/get_comments', getCommentsURL: '/get_comments',
acceptCommentURL: '/accept_comment', acceptCommentURL: '/accept_comment',
rejectCommentURL: '/reject_comment', rejectCommentURL: '/reject_comment',
rejectCommentURL: '/delete_comment', deleteCommentURL: '/delete_comment',
commentImage: '/static/_static/comment.png', commentImage: '/static/_static/comment.png',
closeCommentImage: '/static/_static/comment-close.png',
loadingImage: '/static/_static/ajax-loader.gif', loadingImage: '/static/_static/ajax-loader.gif',
commentBrightImage: '/static/_static/comment-bright.png', commentBrightImage: '/static/_static/comment-bright.png',
upArrow: '/static/_static/up.png', upArrow: '/static/_static/up.png',
@ -715,38 +715,32 @@
</div>'; </div>';
var popupTemplate = '\ var popupTemplate = '\
<div id="popup_template">\ <div class="sphinx_comments" id="sc<%id%>">\
<div class="popup_comment">\
<a id="comment_close" href="#">x</a>\
<h1>Comments</h1>\ <h1>Comments</h1>\
<form method="post" id="comment_form" action="/docs/add_comment">\ <form method="post" id="cf<%id%>" class="comment_form" action="/docs/add_comment">\
<textarea name="comment" cols="80"></textarea>\ <textarea name="comment" cols="80"></textarea>\
<p class="propose_button">\ <p class="propose_button">\
<a href="#" class="show_propose_change">\ <a href="#" id="pc<%id%>" class="show_propose_change">\
Propose a change &#9657;\ Propose a change &#9657;\
</a>\ </a>\
<a href="#" class="hide_propose_change">\ <a href="#" id="hc<%id%>" class="hide_propose_change">\
Propose a change &#9663;\ Propose a change &#9663;\
</a>\ </a>\
</p>\ </p>\
<textarea name="proposal" cols="80" spellcheck="false"></textarea>\ <textarea name="proposal" id="pt<%id%>" cols="80" spellcheck="false"></textarea>\
<input type="submit" value="add comment" id="comment_button" />\ <input type="submit" value="add comment" />\
<input type="hidden" name="node" />\ <input type="hidden" name="node" value="<%id%>" />\
<input type="hidden" name="parent" value="" />\ <input type="hidden" name="parent" value="" />\
<p class="sort_options">\ <p class="sort_options">\
Sort by:\ Sort by:\
<a href="#" class="sort_option" id="rating">top</a>\ <a href="#" class="sort_option rating">top</a>\
<a href="#" class="sort_option" id="ascage">newest</a>\ <a href="#" class="sort_option ascage">newest</a>\
<a href="#" class="sort_option" id="age">oldest</a>\ <a href="#" class="sort_option age">oldest</a>\
</p>\ </p>\
</form>\ </form>\
<h3 id="comment_notification">loading comments... <img src="' + <h3 id="cn<%id%>">loading comments... <img src="<%loadingImage%>" alt="" /></h3>\
opts.loadingImage + '" alt="" /></h3>\ <ul id="cl<%id%>"></ul>\
<ul id="comment_ul"></ul>\ </div>';
</div>\
</div>\
<div id="focuser"></div>';
$(document).ready(function() { $(document).ready(function() {
init(); init();

View File

@ -371,6 +371,7 @@ class WebSupport(object):
if self.staticdir != 'static': if self.staticdir != 'static':
static_urls = [ static_urls = [
('commentImage', 'comment.png'), ('commentImage', 'comment.png'),
('closeCommentImage', 'comment-close.png'),
('loadingImage', 'ajax-loader.gif'), ('loadingImage', 'ajax-loader.gif'),
('commentBrightImage', 'comment-bright.png'), ('commentBrightImage', 'comment-bright.png'),
('upArrow', 'up.png'), ('upArrow', 'up.png'),