#comments-container a(name="comments") section#comments-list .comment-reply-container | {% if current_user.is_authenticated %} | {% if has_method_POST %} .comment-reply-avatar img(src="{{ current_user.gravatar }}") .comment-reply-form .comment-reply-field textarea( id="comment_field", data-parent_id="{{ parent_id }}", placeholder="Join the conversation...",) .comment-reply-meta .comment-details .comment-rules a( title="Markdown Supported" href="https://guides.github.com/features/mastering-markdown/") i.pi-markdown .comment-author span.commenting-as commenting as span.author-name {{ current_user.full_name }} button.comment-action-cancel.btn.btn-outline( type="button", title="Cancel") i.pi-cancel button.comment-action-submit.btn.btn-outline( id="comment_submit", type="button", title="Post Comment") | Post Comment span.hint (Ctrl+Enter) .comment-reply-preview | {% else %} | {# * It's authenticated, but has no 'POST' permission #} .comment-reply-form .comment-reply-field.sign-in textarea( disabled, id="comment_field", data-parent_id="{{ parent_id }}", placeholder="") .sign-in | Join the conversation! Subscribe to Blender Cloud now. | {% endif %} | {% else %} | {# * It's not autenticated #} .comment-reply-form .comment-reply-field.sign-in textarea( disabled, id="comment_field", data-parent_id="{{ parent_id }}", placeholder="") .sign-in a(href="{{ url_for('users.login') }}") Log in | to comment. | {% endif %} section#comments-list-header #comments-list-title #comments-list-items #comments-list-items-loading i.pi-spin script#comment-template(type="text/x-handlebars-template") | {% raw %} | {{#list items }} .comment-container( id="{{ _id }}", data-node_id="{{ _id }}", class="{{#if is_team}}is-team{{/if}}{{#if is_reply}}is-reply{{else}}is-first{{/if}}") .comment-header .comment-avatar img(src="{{ gravatar }}") .comment-author(class="{{#if is_own}}own{{/if}}") | {{ author }} span.username ({{ author_username }}) | {{#if is_team}} .comment-badge.badge-team(title="Project Member") team | {{/if}} .comment-time {{ time_published }} {{#if time_edited }} (edited {{ time_edited }}){{/if}} .comment-content {{{ content }}} | {{#if is_own}} .comment-content-preview | {{/if}} .comment-meta .comment-rating( class="{{#if is_rated}}rated{{/if}}{{#if is_rated_positive}} positive{{/if}}") .comment-rating-value(title="Number of likes") {{ rating }} | {{#unless is_own}} .comment-action-rating.up(title="Like comment") | {{/unless}} .comment-action-reply(title="Reply to this comment") span reply | {{#if is_own}} .comment-action-edit span.edit_mode(title="Edit comment") edit span.edit_save(title="Save comment") i.pi-check | save changes span.edit_cancel(title="Cancel changes") i.pi-cancel | cancel | {{/if}} | {{/list}} | {% endraw %} | {% block comment_scripts %} script. // Markdown initialization var convert = new Markdown.getSanitizingConverter(); Markdown.Extra.init(convert); convert = convert.makeHtml; // Define the template for handlebars var source = $("#comment-template").html(); var template = Handlebars.compile(source); // Register the helper for generating the comments list Handlebars.registerHelper('list', function(context, options) { var ret = ""; var comments_count = 0 // Loop through all first-level comments for(var i=0, j=context.length; i 0) ? comments_count : 'No') + ((comments_count == 1) ? ' comment' : ' comments')); return ret; }); // Helper for the if/else statement Handlebars.registerHelper('if', function(conditional, options) { if(conditional) { return options.fn(this); } else { return options.inverse(this); } }); /* Build the markdown preview when typing in textarea */ $(function() { var $textarea = $('.comment-reply-field textarea'), $container = $('.comment-reply-form'), $preview = $('.comment-reply-preview'); // As we type in the textarea $textarea.keyup(function(e) { // Convert markdown $preview.html(convert($textarea.val())); // While we are at it, style when empty if ($textarea.val()) { $container.addClass('filled'); } else { $container.removeClass('filled'); }; // Send on ctrl+enter if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey){ $( ".comment-action-submit" ).trigger( "click" ); }; }).trigger('keyup'); }); // Get the comments list in JSON $.getJSON( "{{url_for('nodes.comments_index')}}?parent_id={{ parent_id }}&format=json", function( data ) { // Format using handlebars template var comments = template(data); if (comments && comments.trim() !="") { $('#comments-list-items').html(comments); } else { $('#comments-list-items').html(''); } }) .done(function(){ var scrollToId = location.hash; if (scrollToId.length <= 1) return; document.getElementById(scrollToId.substr(1)).scrollIntoView(true); $(scrollToId).addClass('comment-linked'); }); /* Submit comment */ $('.comment-action-submit').click(function(e){ var $this = $(this); var $textarea = $('.comment-reply-field textarea'); var commentField = document.getElementById('comment_field'); var comment = commentField.value; function error(msg) { // No content in the textarea $this.addClass('button-field-error'); $textarea.addClass('field-error') $this.html(msg); setTimeout(function(){ $this.html('Post Comment'); $this.removeClass('button-field-error'); $textarea.removeClass('field-error'); }, 2500); } if (comment.length < 5) { if (comment.length == 0) error("Say something..."); else error("Minimum 5 characters."); return; } $this.addClass('submitting'); $this.html(' Posting...'); // Collect parent_id parent_id = commentField.getAttribute('data-parent_id'); $.post("{{url_for('nodes.comments_create')}}", // Submit content and parent_id for comment creation {'content': comment, 'parent_id': parent_id} ) .fail(function(){ $this.addClass('button-field-error'); $textarea.addClass('field-error') $this.html("Houston! Try again?"); setTimeout(function(){ $this.html('Post Comment'); $this.removeClass('button-field-error'); $textarea.removeClass('field-error'); }, 2500); }) .done(function(){ // Load the comments var url = "{{url_for('nodes.comments_index')}}?parent_id={{ parent_id }}"; $.get(url, function(dataHtml) { // Update the DOM injecting the generate HTML into the page $('#comments-container').replaceWith(dataHtml); }) }); }); /* Edit comment */ // Markdown convert as we type in the textarea $(document).on('keyup','body .comment-content textarea',function(e){ var $textarea = $(this), $container = $(this).parent(), $preview = $container.next(); // Convert markdown $preview.html(convert($textarea.val())); // While we are at it, style if empty if (!$textarea.val()) { $container.addClass('empty'); } else { $container.removeClass('empty'); }; }).trigger('keyup'); /* Enter edit mode */ $(document).on('click','body .comment-action-edit span.edit_mode',function(){ $(this).hide(); $(this).siblings('span.edit_cancel').show(); $(this).siblings('span.edit_save').show(); var comment_content = $(this).parent().parent().siblings('.comment-content'); var comment_id = comment_content.parent().attr('data-node_id'); var height = comment_content.height(); var url = '/nodes/' + comment_id + '/view?format=json'; $.get(url, function(data) { var comment_raw = data['node']['properties']['content']; comment_content.html(''); comment_content.addClass('editing') .find('textarea') .height(height) .focus(); comment_content.siblings('.comment-content-preview').show(); }) .fail(function(data){ statusBarSet('error', 'Error entering edit mode.', 'pi-warning'); }); }); /* Return UI to normal, when cancelling or saving */ function commentEditCancel(comment_container) { var comment_id = comment_container.parent().attr('id'); var url = '/nodes/' + comment_id + '/view?format=json'; $.get(url, function(data) { var comment_raw = data['node']['properties']['content']; comment_container.html(convert(comment_raw)) .removeClass('editing'); comment_container.siblings('.comment-content-preview').html('').hide(); }) .fail(function(data){ statusBarSet('error', 'Error canceling.', 'pi-warning'); }); } $(document).on('click','body .comment-action-edit span.edit_cancel',function(e){ $(this).hide(); $(this).siblings('span.edit_save').hide(); $(this).siblings('span.edit_mode').show(); var commentContainer = $(this).parent().parent().siblings('.comment-content'); commentEditCancel(commentContainer); }); /* Save edited comment */ $(document).on('click','body .comment-action-edit span.edit_save',function(e){ var $this = $(this); var commentContainer = $(this).parent().parent().siblings('.comment-content'); var commentField = commentContainer.find('textarea'); var comment = commentField.val(); var commentId = commentContainer.parent().attr('id'); function error(msg) { // No content in the textarea $this.addClass('error') .html(msg); commentField.addClass('field-error') setTimeout(function(){ $this.html(' save changes') .removeClass('error'); commentField.removeClass('field-error'); }, 2500); } if (comment.length < 5) { if (comment.length == 0) error("Say something..."); else error("Minimum 5 characters."); return; } $this.addClass('saving') .html(' Saving...'); $.post('/nodes/comments/' + commentId, {'content': comment} ) .fail(function(){ $this.addClass('error') .html("Houston! Try again?"); commentField.addClass('field-error') setTimeout(function(){ $this.html('Save changes') .removeClass('error'); commentField.removeClass('field-error'); }, 2500); }) .done(function(){ commentEditCancel(commentContainer); commentContainer .html(convert(comment)); commentContainer.next().text(comment); $this.html(' saved!') .removeClass('saving') .siblings('span.edit_cancel').hide(); setTimeout(function(){ $this.html(' save changes') .hide() .siblings('span.edit_mode').show(); }, 2500); }); }); | {% endblock %}