diff --git a/lms/static/coffee/src/discussion/discussion_router.coffee b/lms/static/coffee/src/discussion/discussion_router.coffee index 9d48ccb08a..53ada7a596 100644 --- a/lms/static/coffee/src/discussion/discussion_router.coffee +++ b/lms/static/coffee/src/discussion/discussion_router.coffee @@ -1,41 +1,42 @@ -class @DiscussionRouter extends Backbone.Router - routes: - "": "allThreads" - ":forum_name/threads/:thread_id" : "showThread" +if Backbone? + class @DiscussionRouter extends Backbone.Router + routes: + "": "allThreads" + ":forum_name/threads/:thread_id" : "showThread" - initialize: (options) -> - @discussion = options['discussion'] - @nav = new DiscussionThreadListView(collection: @discussion, el: $(".sidebar")) - @nav.on "thread:selected", @navigateToThread - @nav.on "thread:removed", @navigateToAllThreads - @nav.on "threads:rendered", @setActiveThread - @nav.render() + initialize: (options) -> + @discussion = options['discussion'] + @nav = new DiscussionThreadListView(collection: @discussion, el: $(".sidebar")) + @nav.on "thread:selected", @navigateToThread + @nav.on "thread:removed", @navigateToAllThreads + @nav.on "threads:rendered", @setActiveThread + @nav.render() - @newPostView = new NewPostView(el: $(".new-post-article"), collection: @discussion) - @newPostView.on "thread:created", @navigateToThread + @newPostView = new NewPostView(el: $(".new-post-article"), collection: @discussion) + @newPostView.on "thread:created", @navigateToThread - allThreads: -> - @nav.updateSidebar() - - setActiveThread: => - if @thread - @nav.setActiveThread(@thread.get("id")) - - showThread: (forum_name, thread_id) -> - @thread = @discussion.get(thread_id) - @setActiveThread() - if(@main) - @main.undelegateEvents() - - @main = new DiscussionThreadView(el: $(".discussion-column"), model: @thread) - @main.render() - @main.on "thread:responses:rendered", => + allThreads: -> @nav.updateSidebar() - navigateToThread: (thread_id) => - thread = @discussion.get(thread_id) - @navigate("#{thread.get("commentable_id")}/threads/#{thread_id}", trigger: true) + setActiveThread: => + if @thread + @nav.setActiveThread(@thread.get("id")) - navigateToAllThreads: => - console.log "navigating" - @navigate("", trigger: true) + showThread: (forum_name, thread_id) -> + @thread = @discussion.get(thread_id) + @setActiveThread() + if(@main) + @main.undelegateEvents() + + @main = new DiscussionThreadView(el: $(".discussion-column"), model: @thread) + @main.render() + @main.on "thread:responses:rendered", => + @nav.updateSidebar() + + navigateToThread: (thread_id) => + thread = @discussion.get(thread_id) + @navigate("#{thread.get("commentable_id")}/threads/#{thread_id}", trigger: true) + + navigateToAllThreads: => + console.log "navigating" + @navigate("", trigger: true) diff --git a/lms/static/coffee/src/discussion/main.coffee b/lms/static/coffee/src/discussion/main.coffee index 6d7818c223..ed31a532c1 100644 --- a/lms/static/coffee/src/discussion/main.coffee +++ b/lms/static/coffee/src/discussion/main.coffee @@ -1,17 +1,18 @@ -DiscussionApp = - start: (elem)-> - # TODO: Perhaps eliminate usage of global variables when possible - element = $(elem) - window.$$course_id = element.data("course-id") - user_info = element.data("user-info") - threads = element.data("threads") - content_info = element.data("content-info") - window.user = new DiscussionUser(user_info) - Content.loadContentInfos(content_info) - discussion = new Discussion(threads) - new DiscussionRouter({discussion: discussion}) - Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"}) +if Backbone? + DiscussionApp = + start: (elem)-> + # TODO: Perhaps eliminate usage of global variables when possible + element = $(elem) + window.$$course_id = element.data("course-id") + user_info = element.data("user-info") + threads = element.data("threads") + content_info = element.data("content-info") + window.user = new DiscussionUser(user_info) + Content.loadContentInfos(content_info) + discussion = new Discussion(threads) + new DiscussionRouter({discussion: discussion}) + Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"}) -$ -> - $("section.discussion").each (index, elem) -> - DiscussionApp.start(elem) + $ -> + $("section.discussion").each (index, elem) -> + DiscussionApp.start(elem) diff --git a/lms/static/coffee/src/discussion/models/discussion_user.coffee b/lms/static/coffee/src/discussion/models/discussion_user.coffee index 6449e80b85..892727c523 100644 --- a/lms/static/coffee/src/discussion/models/discussion_user.coffee +++ b/lms/static/coffee/src/discussion/models/discussion_user.coffee @@ -1,14 +1,15 @@ -class @DiscussionUser extends Backbone.Model - following: (thread) -> - _.include(@get('subscribed_thread_ids'), thread.id) +if Backbone? + class @DiscussionUser extends Backbone.Model + following: (thread) -> + _.include(@get('subscribed_thread_ids'), thread.id) - voted: (thread) -> - _.include(@get('upvoted_ids'), thread.id) + voted: (thread) -> + _.include(@get('upvoted_ids'), thread.id) - vote: (thread) -> - @get('upvoted_ids').push(thread.id) - thread.vote() + vote: (thread) -> + @get('upvoted_ids').push(thread.id) + thread.vote() - unvote: (thread) -> - @set('upvoted_ids', _.without(@get('upvoted_ids'), thread.id)) - thread.unvote() + unvote: (thread) -> + @set('upvoted_ids', _.without(@get('upvoted_ids'), thread.id)) + thread.unvote() diff --git a/lms/static/coffee/src/discussion/user_profile.coffee b/lms/static/coffee/src/discussion/user_profile.coffee index ed4f6ea988..9f0600dd1d 100644 --- a/lms/static/coffee/src/discussion/user_profile.coffee +++ b/lms/static/coffee/src/discussion/user_profile.coffee @@ -1,29 +1,30 @@ -class @DiscussionUserProfileView extends Backbone.View - toggleModeratorStatus: (event) -> - confirmValue = confirm("Are you sure?") - if not confirmValue then return - $elem = $(event.target) - if $elem.hasClass("sidebar-promote-moderator-button") - isModerator = true - else if $elem.hasClass("sidebar-revoke-moderator-button") - isModerator = false - else - console.error "unrecognized moderator status" - return - url = DiscussionUtil.urlFor('update_moderator_status', $$profiled_user_id) - DiscussionUtil.safeAjax - $elem: $elem - url: url - type: "POST" - dataType: 'json' - data: - is_moderator: isModerator - error: (response, textStatus, e) -> - console.log e - success: (response, textStatus) => - parent = @$el.parent() - @$el.replaceWith(response.html) - view = new DiscussionUserProfileView el: parent.children(".user-profile") +if Backbone? + class @DiscussionUserProfileView extends Backbone.View + toggleModeratorStatus: (event) -> + confirmValue = confirm("Are you sure?") + if not confirmValue then return + $elem = $(event.target) + if $elem.hasClass("sidebar-promote-moderator-button") + isModerator = true + else if $elem.hasClass("sidebar-revoke-moderator-button") + isModerator = false + else + console.error "unrecognized moderator status" + return + url = DiscussionUtil.urlFor('update_moderator_status', $$profiled_user_id) + DiscussionUtil.safeAjax + $elem: $elem + url: url + type: "POST" + dataType: 'json' + data: + is_moderator: isModerator + error: (response, textStatus, e) -> + console.log e + success: (response, textStatus) => + parent = @$el.parent() + @$el.replaceWith(response.html) + view = new DiscussionUserProfileView el: parent.children(".user-profile") - events: - "click .sidebar-toggle-moderator-button": "toggleModeratorStatus" + events: + "click .sidebar-toggle-moderator-button": "toggleModeratorStatus" diff --git a/lms/static/coffee/src/discussion/views/discussion_content_view.coffee b/lms/static/coffee/src/discussion/views/discussion_content_view.coffee index 247261c587..98f0eb6137 100644 --- a/lms/static/coffee/src/discussion/views/discussion_content_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_content_view.coffee @@ -1,89 +1,90 @@ -class @DiscussionContentView extends Backbone.View +if Backbone? + class @DiscussionContentView extends Backbone.View - attrRenderer: - endorsed: (endorsed) -> - if endorsed - @$(".action-endorse").addClass("is-endorsed") - else - @$(".action-endorse").removeClass("is-endorsed") - - closed: (closed) -> - return if not @$(".action-openclose").length - return if not @$(".post-status-closed").length - if closed - @$(".post-status-closed").show() - @$(".action-openclose").html(@$(".action-openclose").html().replace("Close", "Open")) - @$(".discussion-reply-new").hide() - else - @$(".post-status-closed").hide() - @$(".action-openclose").html(@$(".action-openclose").html().replace("Open", "Close")) - @$(".discussion-reply-new").show() - - voted: (voted) -> - - votes_point: (votes_point) -> - - comments_count: (comments_count) -> - - subscribed: (subscribed) -> - if subscribed - @$(".dogear").addClass("is-followed") - else - @$(".dogear").removeClass("is-followed") - - ability: (ability) -> - for action, selector of @abilityRenderer - if not ability[action] - selector.disable.apply(@) + attrRenderer: + endorsed: (endorsed) -> + if endorsed + @$(".action-endorse").addClass("is-endorsed") else - selector.enable.apply(@) + @$(".action-endorse").removeClass("is-endorsed") - abilityRenderer: - editable: - enable: -> @$(".action-edit").closest("li").show() - disable: -> @$(".action-edit").closest("li").hide() - can_delete: - enable: -> @$(".action-delete").closest("li").show() - disable: -> @$(".action-delete").closest("li").hide() - can_endorse: - enable: -> @$(".action-endorse").css("cursor", "auto") - disable: -> @$(".action-endorse").css("cursor", "default") - can_openclose: - enable: -> @$(".action-openclose").closest("li").show() - disable: -> @$(".action-openclose").closest("li").hide() + closed: (closed) -> + return if not @$(".action-openclose").length + return if not @$(".post-status-closed").length + if closed + @$(".post-status-closed").show() + @$(".action-openclose").html(@$(".action-openclose").html().replace("Close", "Open")) + @$(".discussion-reply-new").hide() + else + @$(".post-status-closed").hide() + @$(".action-openclose").html(@$(".action-openclose").html().replace("Open", "Close")) + @$(".discussion-reply-new").show() - renderPartialAttrs: -> - for attr, value of @model.changedAttributes() - if @attrRenderer[attr] - @attrRenderer[attr].apply(@, [value]) + voted: (voted) -> - renderAttrs: -> - for attr, value of @model.attributes - if @attrRenderer[attr] - @attrRenderer[attr].apply(@, [value]) + votes_point: (votes_point) -> - $: (selector) -> - @$local.find(selector) + comments_count: (comments_count) -> - initLocal: -> - @$local = @$el.children(".local") - if not @$local.length - @$local = @$el - @$delegateElement = @$local + subscribed: (subscribed) -> + if subscribed + @$(".dogear").addClass("is-followed") + else + @$(".dogear").removeClass("is-followed") - makeWmdEditor: (cls_identifier) => - if not @$el.find(".wmd-panel").length - DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), cls_identifier + ability: (ability) -> + for action, selector of @abilityRenderer + if not ability[action] + selector.disable.apply(@) + else + selector.enable.apply(@) - getWmdEditor: (cls_identifier) => - DiscussionUtil.getWmdEditor @$el, $.proxy(@$, @), cls_identifier + abilityRenderer: + editable: + enable: -> @$(".action-edit").closest("li").show() + disable: -> @$(".action-edit").closest("li").hide() + can_delete: + enable: -> @$(".action-delete").closest("li").show() + disable: -> @$(".action-delete").closest("li").hide() + can_endorse: + enable: -> @$(".action-endorse").css("cursor", "auto") + disable: -> @$(".action-endorse").css("cursor", "default") + can_openclose: + enable: -> @$(".action-openclose").closest("li").show() + disable: -> @$(".action-openclose").closest("li").hide() - getWmdContent: (cls_identifier) => - DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), cls_identifier + renderPartialAttrs: -> + for attr, value of @model.changedAttributes() + if @attrRenderer[attr] + @attrRenderer[attr].apply(@, [value]) - setWmdContent: (cls_identifier, text) => - DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), cls_identifier, text + renderAttrs: -> + for attr, value of @model.attributes + if @attrRenderer[attr] + @attrRenderer[attr].apply(@, [value]) - initialize: -> - @initLocal() - @model.bind('change', @renderPartialAttrs, @) + $: (selector) -> + @$local.find(selector) + + initLocal: -> + @$local = @$el.children(".local") + if not @$local.length + @$local = @$el + @$delegateElement = @$local + + makeWmdEditor: (cls_identifier) => + if not @$el.find(".wmd-panel").length + DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), cls_identifier + + getWmdEditor: (cls_identifier) => + DiscussionUtil.getWmdEditor @$el, $.proxy(@$, @), cls_identifier + + getWmdContent: (cls_identifier) => + DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), cls_identifier + + setWmdContent: (cls_identifier, text) => + DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), cls_identifier, text + + initialize: -> + @initLocal() + @model.bind('change', @renderPartialAttrs, @) diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_inline_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_inline_view.coffee index 1c0dbc996f..cddd9e556c 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_inline_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_inline_view.coffee @@ -1,213 +1,214 @@ -class @DiscussionThreadInlineView extends DiscussionContentView - expanded = false - events: - "click .discussion-vote": "toggleVote" - "click .action-follow": "toggleFollowing" - "click .discussion-submit-post": "submitComment" - "click .action-edit": "edit" - "click .action-delete": "delete" - "click .action-openclose": "toggleClosed" - "click .expand-post": "expandPost" - "click .collapse-post": "collapsePost" +if Backbone? + class @DiscussionThreadInlineView extends DiscussionContentView + expanded = false + events: + "click .discussion-vote": "toggleVote" + "click .action-follow": "toggleFollowing" + "click .discussion-submit-post": "submitComment" + "click .action-edit": "edit" + "click .action-delete": "delete" + "click .action-openclose": "toggleClosed" + "click .expand-post": "expandPost" + "click .collapse-post": "collapsePost" - template: -> DiscussionUtil.getTemplate("_inline_thread") + template: -> DiscussionUtil.getTemplate("_inline_thread") - initLocal: -> - @$local = @$el.children(".discussion-article").children(".local") - @$delegateElement = @$local + initLocal: -> + @$local = @$el.children(".discussion-article").children(".local") + @$delegateElement = @$local - initialize: -> - super() - @model.on "change", @updateModelDetails + initialize: -> + super() + @model.on "change", @updateModelDetails - render: -> - if not @model.has('abbreviatedBody') - @abbreviateBody() - @$el.html(Mustache.render(@template(), $.extend(@model.toJSON(),{expanded: @expanded}) )) - @initLocal() - @delegateEvents() - @renderDogear() - @renderVoted() - @renderAttrs() - @$("span.timeago").timeago() - @convertMath() - if @expanded + render: -> + if not @model.has('abbreviatedBody') + @abbreviateBody() + @$el.html(Mustache.render(@template(), $.extend(@model.toJSON(),{expanded: @expanded}) )) + @initLocal() + @delegateEvents() + @renderDogear() + @renderVoted() + @renderAttrs() + @$("span.timeago").timeago() + @convertMath() + if @expanded + @makeWmdEditor "reply-body" + @renderResponses() + @ + + renderDogear: -> + if window.user.following(@model) + @$(".dogear").addClass("is-followed") + + renderVoted: => + if window.user.voted(@model) + @$("[data-role=discussion-vote]").addClass("is-cast") + else + @$("[data-role=discussion-vote]").removeClass("is-cast") + + updateModelDetails: => + @renderVoted() + @$("[data-role=discussion-vote] .votes-count-number").html(@model.get("votes")["up_count"]) + + convertMath: -> + element = @$(".post-body") + element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.html() + MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] + + renderResponses: -> + DiscussionUtil.safeAjax + url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}" + $loading: @$el + success: (data, textStatus, xhr) => + @$el.find(".loading").remove() + Content.loadContentInfos(data['annotated_content_info']) + comments = new Comments(data['content']['children']) + comments.each @renderResponse + @trigger "thread:responses:rendered" + + renderResponse: (response) => + response.set('thread', @model) + view = new ThreadResponseView(model: response) + view.on "comment:add", @addComment + view.render() + @$el.find(".responses").append(view.el) + + addComment: => + @model.comment() + + toggleVote: (event) -> + event.preventDefault() + if window.user.voted(@model) + @unvote() + else + @vote() + + toggleFollowing: (event) -> + $elem = $(event.target) + url = null + console.log "follow" + if not @model.get('subscribed') + @model.follow() + url = @model.urlFor("follow") + else + @model.unfollow() + url = @model.urlFor("unfollow") + DiscussionUtil.safeAjax + $elem: $elem + url: url + type: "POST" + + vote: -> + window.user.vote(@model) + url = @model.urlFor("upvote") + DiscussionUtil.safeAjax + $elem: @$(".discussion-vote") + url: url + type: "POST" + success: (response, textStatus) => + if textStatus == 'success' + @model.set(response) + + unvote: -> + window.user.unvote(@model) + url = @model.urlFor("unvote") + DiscussionUtil.safeAjax + $elem: @$(".discussion-vote") + url: url + type: "POST" + success: (response, textStatus) => + if textStatus == 'success' + @model.set(response) + + submitComment: (event) -> + event.preventDefault() + url = @model.urlFor('reply') + body = @getWmdContent("reply-body") + return if not body.trim().length + @setWmdContent("reply-body", "") + comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"), votes: { up_count: 0 }, endorsed: false, user_id: window.user.get("id")) + comment.set('thread', @model.get('thread')) + @renderResponse(comment) + @model.addComment() + + DiscussionUtil.safeAjax + $elem: $(event.target) + url: url + type: "POST" + dataType: 'json' + data: + body: body + success: (data, textStatus) => + comment.updateInfo(data.annotated_content_info) + comment.set(data.content) + + edit: -> + + + delete: (event) -> + url = @model.urlFor('delete') + if not @model.can('can_delete') + return + if not confirm "Are you sure to delete thread \"#{@model.get('title')}\"?" + return + @model.remove() + @$el.empty() + $elem = $(event.target) + DiscussionUtil.safeAjax + $elem: $elem + url: url + type: "POST" + success: (response, textStatus) => + + toggleClosed: (event) -> + $elem = $(event.target) + url = @model.urlFor('close') + closed = @model.get('closed') + data = { closed: not closed } + DiscussionUtil.safeAjax + $elem: $elem + url: url + data: data + type: "POST" + success: (response, textStatus) => + @model.set('closed', not closed) + @model.set('ability', response.ability) + + toggleEndorse: (event) -> + $elem = $(event.target) + url = @model.urlFor('endorse') + endorsed = @model.get('endorsed') + data = { endorsed: not endorsed } + DiscussionUtil.safeAjax + $elem: $elem + url: url + data: data + type: "POST" + success: (response, textStatus) => + @model.set('endorsed', not endorsed) + + abbreviateBody: -> + abbreviated = DiscussionUtil.abbreviateString @model.get('body'), 140 + @model.set('abbreviatedBody', abbreviated) + + expandPost: (event) -> + @expanded = true + @$el.addClass('expanded') + @$el.find('.post-body').html(@model.get('body')) + @convertMath() + @$el.find('.expand-post').css('display', 'none') + @$el.find('.collapse-post').css('display', 'block') + @$el.find('.post-extended-content').show() @makeWmdEditor "reply-body" - @renderResponses() - @ + if @$el.find('.loading').length + @renderResponses() - renderDogear: -> - if window.user.following(@model) - @$(".dogear").addClass("is-followed") - - renderVoted: => - if window.user.voted(@model) - @$("[data-role=discussion-vote]").addClass("is-cast") - else - @$("[data-role=discussion-vote]").removeClass("is-cast") - - updateModelDetails: => - @renderVoted() - @$("[data-role=discussion-vote] .votes-count-number").html(@model.get("votes")["up_count"]) - - convertMath: -> - element = @$(".post-body") - element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.html() - MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] - - renderResponses: -> - DiscussionUtil.safeAjax - url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}" - $loading: @$el - success: (data, textStatus, xhr) => - @$el.find(".loading").remove() - Content.loadContentInfos(data['annotated_content_info']) - comments = new Comments(data['content']['children']) - comments.each @renderResponse - @trigger "thread:responses:rendered" - - renderResponse: (response) => - response.set('thread', @model) - view = new ThreadResponseView(model: response) - view.on "comment:add", @addComment - view.render() - @$el.find(".responses").append(view.el) - - addComment: => - @model.comment() - - toggleVote: (event) -> - event.preventDefault() - if window.user.voted(@model) - @unvote() - else - @vote() - - toggleFollowing: (event) -> - $elem = $(event.target) - url = null - console.log "follow" - if not @model.get('subscribed') - @model.follow() - url = @model.urlFor("follow") - else - @model.unfollow() - url = @model.urlFor("unfollow") - DiscussionUtil.safeAjax - $elem: $elem - url: url - type: "POST" - - vote: -> - window.user.vote(@model) - url = @model.urlFor("upvote") - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) - - unvote: -> - window.user.unvote(@model) - url = @model.urlFor("unvote") - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) - - submitComment: (event) -> - event.preventDefault() - url = @model.urlFor('reply') - body = @getWmdContent("reply-body") - return if not body.trim().length - @setWmdContent("reply-body", "") - comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"), votes: { up_count: 0 }, endorsed: false, user_id: window.user.get("id")) - comment.set('thread', @model.get('thread')) - @renderResponse(comment) - @model.addComment() - - DiscussionUtil.safeAjax - $elem: $(event.target) - url: url - type: "POST" - dataType: 'json' - data: - body: body - success: (data, textStatus) => - comment.updateInfo(data.annotated_content_info) - comment.set(data.content) - - edit: -> - - - delete: (event) -> - url = @model.urlFor('delete') - if not @model.can('can_delete') - return - if not confirm "Are you sure to delete thread \"#{@model.get('title')}\"?" - return - @model.remove() - @$el.empty() - $elem = $(event.target) - DiscussionUtil.safeAjax - $elem: $elem - url: url - type: "POST" - success: (response, textStatus) => - - toggleClosed: (event) -> - $elem = $(event.target) - url = @model.urlFor('close') - closed = @model.get('closed') - data = { closed: not closed } - DiscussionUtil.safeAjax - $elem: $elem - url: url - data: data - type: "POST" - success: (response, textStatus) => - @model.set('closed', not closed) - @model.set('ability', response.ability) - - toggleEndorse: (event) -> - $elem = $(event.target) - url = @model.urlFor('endorse') - endorsed = @model.get('endorsed') - data = { endorsed: not endorsed } - DiscussionUtil.safeAjax - $elem: $elem - url: url - data: data - type: "POST" - success: (response, textStatus) => - @model.set('endorsed', not endorsed) - - abbreviateBody: -> - abbreviated = DiscussionUtil.abbreviateString @model.get('body'), 140 - @model.set('abbreviatedBody', abbreviated) - - expandPost: (event) -> - @expanded = true - @$el.addClass('expanded') - @$el.find('.post-body').html(@model.get('body')) - @convertMath() - @$el.find('.expand-post').css('display', 'none') - @$el.find('.collapse-post').css('display', 'block') - @$el.find('.post-extended-content').show() - @makeWmdEditor "reply-body" - if @$el.find('.loading').length - @renderResponses() - - collapsePost: (event) -> - @expanded = false - @$el.removeClass('expanded') - @$el.find('.post-body').html(@model.get('abbreviatedBody')) - @convertMath() - @$el.find('.collapse-post').css('display', 'none') - @$el.find('.post-extended-content').hide() - @$el.find('.expand-post').css('display', 'block') + collapsePost: (event) -> + @expanded = false + @$el.removeClass('expanded') + @$el.find('.post-body').html(@model.get('abbreviatedBody')) + @convertMath() + @$el.find('.collapse-post').css('display', 'none') + @$el.find('.post-extended-content').hide() + @$el.find('.expand-post').css('display', 'block') diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index 08f9fb754f..367803f6bd 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -1,269 +1,266 @@ -class @DiscussionThreadListView extends Backbone.View - template: _.template($("#thread-list-template").html()) - events: - "click .search": "showSearch" - "click .browse": "toggleTopicDrop" - "keydown .post-search-field": "performSearch" - "click .sort-bar a": "sortThreads" - "click .browse-topic-drop-menu": "filterTopic" - "click .browse-topic-drop-search-input": "ignoreClick" - "click .post-list a": "threadSelected" +if Backbone? + class @DiscussionThreadListView extends Backbone.View + template: _.template($("#thread-list-template").html()) + events: + "click .search": "showSearch" + "click .browse": "toggleTopicDrop" + "keydown .post-search-field": "performSearch" + "click .sort-bar a": "sortThreads" + "click .browse-topic-drop-menu": "filterTopic" + "click .browse-topic-drop-search-input": "ignoreClick" + "click .post-list a": "threadSelected" - initialize: -> - @displayedCollection = new Discussion(@collection.models) - @collection.on "change", @reloadDisplayedCollection - @collection.on "add", @addAndSelectThread - @sidebar_padding = 10 - @sidebar_header_height = 87 - @boardName + initialize: -> + @displayedCollection = new Discussion(@collection.models) + @collection.on "change", @reloadDisplayedCollection + @collection.on "add", @addAndSelectThread + @sidebar_padding = 10 + @sidebar_header_height = 87 + @boardName - reloadDisplayedCollection: (thread) => - thread_id = thread.get('id') - content = @renderThread(thread) - current_el = @$("a[data-id=#{thread_id}]") - active = current_el.hasClass("active") - current_el.replaceWith(content) - if active - @setActiveThread(thread_id) - - addAndSelectThread: (thread) => - commentable_id = thread.get("commentable_id") - commentable = @$(".board-name[data-discussion_id]").filter(-> $(this).data("discussion_id").id == commentable_id) - commentable.click() - @displayedCollection.add thread - content = @renderThread(thread) - $(".post-list").prepend content - content.wrap("
  • ") - content.click() - - updateSidebar: => - - scrollTop = $(window).scrollTop(); - windowHeight = $(window).height(); - - discussionBody = $(".discussion-article") - discussionsBodyTop = if discussionBody[0] then discussionBody.offset().top - discussionsBodyBottom = discussionsBodyTop + discussionBody.outerHeight() - - sidebar = $(".sidebar") - if scrollTop > discussionsBodyTop - @sidebar_padding - sidebar.addClass('fixed'); - sidebar.css('top', @sidebar_padding); - else - sidebar.removeClass('fixed'); - sidebar.css('top', '0'); - - sidebarWidth = .31 * $(".discussion-body").width(); - sidebar.css('width', sidebarWidth + 'px'); - - sidebarHeight = windowHeight - Math.max(discussionsBodyTop - scrollTop, @sidebar_padding) - - topOffset = scrollTop + windowHeight - discussionBottomOffset = discussionsBodyBottom + @sidebar_padding - amount = Math.max(topOffset - discussionBottomOffset, 0) - - sidebarHeight = sidebarHeight - @sidebar_padding - amount - sidebarHeight = Math.min(Math.max(sidebarHeight, 400), discussionBody.outerHeight()) - sidebar.css 'height', sidebarHeight - - postListWrapper = @$('.post-list-wrapper') - postListWrapper.css('height', (sidebarHeight - @sidebar_header_height - 4) + 'px') - - - # Because we want the behavior that when the body is clicked the menu is - # closed, we need to ignore clicks in the search field and stop propagation. - # Without this, clicking the search field would also close the menu. - ignoreClick: (event) -> - event.stopPropagation() - - render: -> - @timer = 0 - @$el.html(@template()) - - $(window).bind "scroll", @updateSidebar - $(window).bind "resize", @updateSidebar - - @displayedCollection.on "reset", @renderThreads - @displayedCollection.on "thread:remove", @renderThreads - @renderThreads() - @ - - renderThreads: => - @$(".post-list").html("") - rendered = $("
    ") - for thread in @displayedCollection.models + reloadDisplayedCollection: (thread) => + thread_id = thread.get('id') content = @renderThread(thread) - rendered.append content + current_el = @$("a[data-id=#{thread_id}]") + active = current_el.hasClass("active") + current_el.replaceWith(content) + if active + @setActiveThread(thread_id) + + addAndSelectThread: (thread) => + commentable_id = thread.get("commentable_id") + commentable = @$(".board-name[data-discussion_id]").filter(-> $(this).data("discussion_id").id == commentable_id) + commentable.click() + @displayedCollection.add thread + content = @renderThread(thread) + $(".post-list").prepend content content.wrap("
  • ") + content.click() - @$(".post-list").html(rendered.html()) - @trigger "threads:rendered" + updateSidebar: => - renderThread: (thread) => - content = $(_.template($("#thread-list-item-template").html())(thread.toJSON())) - if thread.get('subscribed') - content.addClass("followed") - if thread.get('endorsed') - content.addClass("resolved") - @highlight(content) + scrollTop = $(window).scrollTop(); + windowHeight = $(window).height(); + + discussionBody = $(".discussion-article") + discussionsBodyTop = if discussionBody[0] then discussionBody.offset().top + discussionsBodyBottom = discussionsBodyTop + discussionBody.outerHeight() + + sidebar = $(".sidebar") + if scrollTop > discussionsBodyTop - @sidebar_padding + sidebar.addClass('fixed'); + sidebar.css('top', @sidebar_padding); + else + sidebar.removeClass('fixed'); + sidebar.css('top', '0'); + + sidebarWidth = .31 * $(".discussion-body").width(); + sidebar.css('width', sidebarWidth + 'px'); + + sidebarHeight = windowHeight - Math.max(discussionsBodyTop - scrollTop, @sidebar_padding) + + topOffset = scrollTop + windowHeight + discussionBottomOffset = discussionsBodyBottom + @sidebar_padding + amount = Math.max(topOffset - discussionBottomOffset, 0) + + sidebarHeight = sidebarHeight - @sidebar_padding - amount + sidebarHeight = Math.min(Math.max(sidebarHeight, 400), discussionBody.outerHeight()) + sidebar.css 'height', sidebarHeight + + postListWrapper = @$('.post-list-wrapper') + postListWrapper.css('height', (sidebarHeight - @sidebar_header_height - 4) + 'px') - highlight: (el) -> - el.html(el.html().replace(/<mark>/g, "").replace(/<\/mark>/g, "")) + # Because we want the behavior that when the body is clicked the menu is + # closed, we need to ignore clicks in the search field and stop propagation. + # Without this, clicking the search field would also close the menu. + ignoreClick: (event) -> + event.stopPropagation() - renderThreadListItem: (thread) => - view = new ThreadListItemView(model: thread) - view.on "thread:selected", @threadSelected - view.on "thread:removed", @threadRemoved - view.render() - @$(".post-list").append(view.el) + render: -> + @timer = 0 + @$el.html(@template()) - threadSelected: (e) => - thread_id = $(e.target).closest("a").data("id") - @setActiveThread(thread_id) - @trigger("thread:selected", thread_id) - false + $(window).bind "scroll", @updateSidebar + $(window).bind "resize", @updateSidebar - threadRemoved: (thread_id) => - @trigger("thread:removed", thread_id) + @displayedCollection.on "reset", @renderThreads + @displayedCollection.on "thread:remove", @renderThreads + @renderThreads() + @ - setActiveThread: (thread_id) -> - @$("a[data-id!='#{thread_id}']").removeClass("active") - @$("a[data-id='#{thread_id}']").addClass("active") + renderThreads: => + @$(".post-list").html("") + rendered = $("
    ") + for thread in @displayedCollection.models + content = @renderThread(thread) + rendered.append content + content.wrap("
  • ") - showSearch: -> - @$(".search").addClass('is-open') - @$(".browse").removeClass('is-open') - setTimeout (-> @$(".post-search-field").focus()), 200 + @$(".post-list").html(rendered.html()) + @trigger "threads:rendered" - toggleTopicDrop: (event) => - event.stopPropagation() - @$(".browse").toggleClass('is-dropped') - if @$(".browse").hasClass('is-dropped') - @$(".browse-topic-drop-menu-wrapper").show() - $(".browse-topic-drop-search-input").focus() - $("body").bind "click", @toggleTopicDrop - $("body").bind "keydown", @setActiveItem - else - @$(".browse-topic-drop-menu-wrapper").hide() - $("body").unbind "click", @toggleTopicDrop - $("body").unbind "keydown", @setActiveItem + renderThread: (thread) => + content = $(_.template($("#thread-list-item-template").html())(thread.toJSON())) + if thread.get('subscribed') + content.addClass("followed") + if thread.get('endorsed') + content.addClass("resolved") + @highlight(content) - setTopic: (event) -> - item = $(event.target).closest('a') - boardName = item.find(".board-name").html() - _.each item.parents('ul').not('.browse-topic-drop-menu'), (parent) -> - boardName = $(parent).siblings('a').find('.board-name').html() + ' / ' + boardName - @$(".current-board").html(@fitName(boardName)) - fontSize = 16 - @$(".current-board").css('font-size', '16px') - while @$(".current-board").width() > (@$el.width() * .8) - 40 - fontSize-- - if fontSize < 11 - break - @$(".current-board").css('font-size', fontSize + 'px') - setSelectedTopic: (name) -> - @$(".current-board").html(@fitName(name)) + highlight: (el) -> + el.html(el.html().replace(/<mark>/g, "").replace(/<\/mark>/g, "")) - getNameWidth: (name) -> - test = $("
    ") - test.css - "font-size": @$(".current-board").css('font-size') - opacity: 0 - position: 'absolute' - left: -1000 - top: -1000 - $("body").append(test) - test.html(name) - width = test.width() - test.remove() - return width + renderThreadListItem: (thread) => + view = new ThreadListItemView(model: thread) + view.on "thread:selected", @threadSelected + view.on "thread:removed", @threadRemoved + view.render() + @$(".post-list").append(view.el) + + threadSelected: (e) => + thread_id = $(e.target).closest("a").data("id") + @setActiveThread(thread_id) + @trigger("thread:selected", thread_id) + false + + threadRemoved: (thread_id) => + @trigger("thread:removed", thread_id) + + setActiveThread: (thread_id) -> + @$("a[data-id!='#{thread_id}']").removeClass("active") + @$("a[data-id='#{thread_id}']").addClass("active") + + showSearch: -> + @$(".search").addClass('is-open') + @$(".browse").removeClass('is-open') + setTimeout (-> @$(".post-search-field").focus()), 200 + + toggleTopicDrop: (event) => + event.stopPropagation() + @$(".browse").toggleClass('is-dropped') + if @$(".browse").hasClass('is-dropped') + @$(".browse-topic-drop-menu-wrapper").show() + $(".browse-topic-drop-search-input").focus() + $("body").bind "click", @toggleTopicDrop + $("body").bind "keydown", @setActiveItem + else + @$(".browse-topic-drop-menu-wrapper").hide() + $("body").unbind "click", @toggleTopicDrop + $("body").unbind "keydown", @setActiveItem + + setTopic: (event) -> + item = $(event.target).closest('a') + boardName = item.find(".board-name").html() + _.each item.parents('ul').not('.browse-topic-drop-menu'), (parent) -> + boardName = $(parent).siblings('a').find('.board-name').html() + ' / ' + boardName + @$(".current-board").html(@fitName(boardName)) + fontSize = 16 + @$(".current-board").css('font-size', '16px') + while @$(".current-board").width() > (@$el.width() * .8) - 40 + fontSize-- + if fontSize < 11 + break + @$(".current-board").css('font-size', fontSize + 'px') + + setSelectedTopic: (name) -> + @$(".current-board").html(@fitName(name)) + + getNameWidth: (name) -> + test = $("
    ") + test.css + "font-size": @$(".current-board").css('font-size') + opacity: 0 + position: 'absolute' + left: -1000 + top: -1000 + $("body").append(test) + test.html(name) + width = test.width() + test.remove() + return width + + fitName: (name) -> + width = @getNameWidth(name) + if width < @maxNameWidth + return name + path = (x.replace /^\s+|\s+$/g, "" for x in name.split("/")) + while path.length > 1 + path.shift() + partialName = "... / " + path.join(" / ") + if @getNameWidth(partialName) < @maxNameWidth + return partialName + + rawName = path[0] + + name = "... / " + rawName + + while @getNameWidth(name) > @maxNameWidth + rawName = rawName[0...rawName.length-1] + name = "... / " + rawName + " ..." - fitName: (name) -> - width = @getNameWidth(name) - if width < @maxNameWidth return name - path = (x.replace /^\s+|\s+$/g, "" for x in name.split("/")) - while path.length > 1 - path.shift() - partialName = "... / " + path.join(" / ") - if @getNameWidth(partialName) < @maxNameWidth - return partialName - rawName = path[0] + filterTopic: (event) -> + @setTopic(event) + item = $(event.target).closest('li') + if item.find("span.board-name").data("discussion_id") == "#all" + item = item.parent() + discussionIds = _.map item.find(".board-name[data-discussion_id]"), (board) -> $(board).data("discussion_id").id + filtered = @collection.filter (thread) => + _.include(discussionIds, thread.get('commentable_id')) + @displayedCollection.reset filtered - name = "... / " + rawName + sortThreads: (event) -> + @$(".sort-bar a").removeClass("active") + $(event.target).addClass("active") + sortBy = $(event.target).data("sort") + if sortBy == "date" + @displayedCollection.comparator = @displayedCollection.sortByDateRecentFirst + else if sortBy == "votes" + @displayedCollection.comparator = @displayedCollection.sortByVotes + else if sortBy == "comments" + @displayedCollection.comparator = @displayedCollection.sortByComments + @displayedCollection.sort() - while @getNameWidth(name) > @maxNameWidth - rawName = rawName[0...rawName.length-1] - name = "... / " + rawName + " ..." + performSearch: (event) -> + if event.which == 13 + event.preventDefault() + url = DiscussionUtil.urlFor("search") + text = @$(".post-search-field").val() + DiscussionUtil.safeAjax + $elem: @$(".post-search-field") + data: { text: text } + url: url + type: "GET" + success: (response, textStatus) => + if textStatus == 'success' + @collection.reset(response.discussion_data) + @displayedCollection.reset(@collection.models) - return name - - filterTopic: (event) -> - @setTopic(event) - item = $(event.target).closest('li') - if item.find("span.board-name").data("discussion_id") == "#all" - item = item.parent() - discussionIds = _.map item.find(".board-name[data-discussion_id]"), (board) -> $(board).data("discussion_id").id - filtered = @collection.filter (thread) => - _.include(discussionIds, thread.get('commentable_id')) - @displayedCollection.reset filtered - - sortThreads: (event) -> - @$(".sort-bar a").removeClass("active") - $(event.target).addClass("active") - sortBy = $(event.target).data("sort") - if sortBy == "date" - @displayedCollection.comparator = @displayedCollection.sortByDateRecentFirst - else if sortBy == "votes" - @displayedCollection.comparator = @displayedCollection.sortByVotes - else if sortBy == "comments" - @displayedCollection.comparator = @displayedCollection.sortByComments - @displayedCollection.sort() - - performSearch: (event) -> - if event.which == 13 + setActiveItem: (event) -> + if event.which == 13 + $(".browse-topic-drop-menu-wrapper .focused").click() + return + if event.which != 40 && event.which != 38 + return event.preventDefault() - url = DiscussionUtil.urlFor("search") - text = @$(".post-search-field").val() - DiscussionUtil.safeAjax - $elem: @$(".post-search-field") - data: { text: text } - url: url - type: "GET" - success: (response, textStatus) => - if textStatus == 'success' - @collection.reset(response.discussion_data) - @displayedCollection.reset(@collection.models) - - setActiveItem: (event) -> - if event.which == 13 - $(".browse-topic-drop-menu-wrapper .focused").click() - return - if event.which != 40 && event.which != 38 - return - event.preventDefault() - - items = $.makeArray($(".browse-topic-drop-menu-wrapper a").not(".hidden")) - index = items.indexOf($('.browse-topic-drop-menu-wrapper .focused')[0]) - - if event.which == 40 - index = Math.min(index + 1, items.length - 1) - if event.which == 38 - index = Math.max(index - 1, 0) - - $(".browse-topic-drop-menu-wrapper .focused").removeClass("focused") - $(items[index]).addClass("focused") - - itemTop = $(items[index]).parent().offset().top - scrollTop = $(".browse-topic-drop-menu").scrollTop() - itemFromTop = $(".browse-topic-drop-menu").offset().top - itemTop - scrollTarget = Math.min(scrollTop - itemFromTop, scrollTop) - scrollTarget = Math.max(scrollTop - itemFromTop - $(".browse-topic-drop-menu").height() + $(items[index]).height(), scrollTarget) - $(".browse-topic-drop-menu").scrollTop(scrollTarget) + items = $.makeArray($(".browse-topic-drop-menu-wrapper a").not(".hidden")) + index = items.indexOf($('.browse-topic-drop-menu-wrapper .focused')[0]) + if event.which == 40 + index = Math.min(index + 1, items.length - 1) + if event.which == 38 + index = Math.max(index - 1, 0) + $(".browse-topic-drop-menu-wrapper .focused").removeClass("focused") + $(items[index]).addClass("focused") + itemTop = $(items[index]).parent().offset().top + scrollTop = $(".browse-topic-drop-menu").scrollTop() + itemFromTop = $(".browse-topic-drop-menu").offset().top - itemTop + scrollTarget = Math.min(scrollTop - itemFromTop, scrollTop) + scrollTarget = Math.max(scrollTop - itemFromTop - $(".browse-topic-drop-menu").height() + $(items[index]).height(), scrollTarget) + $(".browse-topic-drop-menu").scrollTop(scrollTarget) diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee index 8d97c4b74e..8bb207da2b 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee @@ -1,93 +1,94 @@ -class @DiscussionThreadView extends DiscussionContentView +if Backbone? + class @DiscussionThreadView extends DiscussionContentView - events: - "click .discussion-submit-post": "submitComment" + events: + "click .discussion-submit-post": "submitComment" - template: _.template($("#thread-template").html()) + template: _.template($("#thread-template").html()) - $: (selector) -> - @$el.find(selector) + $: (selector) -> + @$el.find(selector) - initialize: -> - super() - @showView = new DiscussionThreadShowView(model: @model) - @showView.bind "thread:delete", @delete - @showView.bind "thread:edit", @edit + initialize: -> + super() + @showView = new DiscussionThreadShowView(model: @model) + @showView.bind "thread:delete", @delete + @showView.bind "thread:edit", @edit - render: -> - @$el.html(@template(@model.toJSON())) - @delegateEvents() + render: -> + @$el.html(@template(@model.toJSON())) + @delegateEvents() - @showView.setElement(@$('.thread-content-wrapper')) - @showView.render() - @showView.delegateEvents() + @showView.setElement(@$('.thread-content-wrapper')) + @showView.render() + @showView.delegateEvents() - @renderAttrs() - @$("span.timeago").timeago() - @makeWmdEditor "reply-body" - @renderResponses() - @ + @renderAttrs() + @$("span.timeago").timeago() + @makeWmdEditor "reply-body" + @renderResponses() + @ - renderResponses: -> - DiscussionUtil.safeAjax - url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}" - success: (data, textStatus, xhr) => - @$el.find(".loading").remove() - Content.loadContentInfos(data['annotated_content_info']) - comments = new Comments(data['content']['children']) - comments.each @renderResponse - @trigger "thread:responses:rendered" + renderResponses: -> + DiscussionUtil.safeAjax + url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}" + success: (data, textStatus, xhr) => + @$el.find(".loading").remove() + Content.loadContentInfos(data['annotated_content_info']) + comments = new Comments(data['content']['children']) + comments.each @renderResponse + @trigger "thread:responses:rendered" - renderResponse: (response) => - response.set('thread', @model) - view = new ThreadResponseView(model: response) - view.on "comment:add", @addComment - view.on "comment:endorse", @endorseThread - view.render() - @$el.find(".responses").append(view.el) + renderResponse: (response) => + response.set('thread', @model) + view = new ThreadResponseView(model: response) + view.on "comment:add", @addComment + view.on "comment:endorse", @endorseThread + view.render() + @$el.find(".responses").append(view.el) - addComment: => - @model.comment() + addComment: => + @model.comment() - endorseThread: (endorsed) => - is_endorsed = @$el.find(".is-endorsed").length - @model.set 'endorsed', is_endorsed + endorseThread: (endorsed) => + is_endorsed = @$el.find(".is-endorsed").length + @model.set 'endorsed', is_endorsed - submitComment: (event) -> - event.preventDefault() - url = @model.urlFor('reply') - body = @getWmdContent("reply-body") - return if not body.trim().length - @setWmdContent("reply-body", "") - comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"), votes: { up_count: 0 }, endorsed: false, user_id: window.user.get("id")) - comment.set('thread', @model.get('thread')) - @renderResponse(comment) - @model.addComment() + submitComment: (event) -> + event.preventDefault() + url = @model.urlFor('reply') + body = @getWmdContent("reply-body") + return if not body.trim().length + @setWmdContent("reply-body", "") + comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"), votes: { up_count: 0 }, endorsed: false, user_id: window.user.get("id")) + comment.set('thread', @model.get('thread')) + @renderResponse(comment) + @model.addComment() - DiscussionUtil.safeAjax - $elem: $(event.target) - url: url - type: "POST" - dataType: 'json' - data: - body: body - success: (data, textStatus) => - comment.updateInfo(data.annotated_content_info) - comment.set(data.content) + DiscussionUtil.safeAjax + $elem: $(event.target) + url: url + type: "POST" + dataType: 'json' + data: + body: body + success: (data, textStatus) => + comment.updateInfo(data.annotated_content_info) + comment.set(data.content) - edit: -> + edit: -> - delete: (event) -> - url = @model.urlFor('delete') - if not @model.can('can_delete') - return - if not confirm "Are you sure to delete thread \"#{@model.get('title')}\"?" - return - @model.remove() - @$el.empty() - $elem = $(event.target) - DiscussionUtil.safeAjax - $elem: $elem - url: url - type: "POST" - success: (response, textStatus) => + delete: (event) -> + url = @model.urlFor('delete') + if not @model.can('can_delete') + return + if not confirm "Are you sure to delete thread \"#{@model.get('title')}\"?" + return + @model.remove() + @$el.empty() + $elem = $(event.target) + DiscussionUtil.safeAjax + $elem: $elem + url: url + type: "POST" + success: (response, textStatus) => diff --git a/lms/static/coffee/src/discussion/views/new_post_inline_vew.coffee b/lms/static/coffee/src/discussion/views/new_post_inline_vew.coffee index f3b04d14ce..4b00cd25f0 100644 --- a/lms/static/coffee/src/discussion/views/new_post_inline_vew.coffee +++ b/lms/static/coffee/src/discussion/views/new_post_inline_vew.coffee @@ -1,55 +1,56 @@ -class @NewPostInlineView extends Backbone.View +if Backbone? + class @NewPostInlineView extends Backbone.View - initialize: () -> + initialize: () -> - @topicId = @$(".topic").first().data("discussion-id") + @topicId = @$(".topic").first().data("discussion-id") - @maxNameWidth = 100 + @maxNameWidth = 100 - DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body" - @$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions() + DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body" + @$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions() - events: - "submit .new-post-form": "createPost" + events: + "submit .new-post-form": "createPost" - # Because we want the behavior that when the body is clicked the menu is - # closed, we need to ignore clicks in the search field and stop propagation. - # Without this, clicking the search field would also close the menu. - ignoreClick: (event) -> - event.stopPropagation() + # Because we want the behavior that when the body is clicked the menu is + # closed, we need to ignore clicks in the search field and stop propagation. + # Without this, clicking the search field would also close the menu. + ignoreClick: (event) -> + event.stopPropagation() - createPost: (event) -> - event.preventDefault() - title = @$(".new-post-title").val() - body = @$(".new-post-body").find(".wmd-input").val() - tags = @$(".new-post-tags").val() + createPost: (event) -> + event.preventDefault() + title = @$(".new-post-title").val() + body = @$(".new-post-body").find(".wmd-input").val() + tags = @$(".new-post-tags").val() - anonymous = false || @$("input.discussion-anonymous").is(":checked") - follow = false || @$("input.discussion-follow").is(":checked") + anonymous = false || @$("input.discussion-anonymous").is(":checked") + follow = false || @$("input.discussion-follow").is(":checked") - url = DiscussionUtil.urlFor('create_thread', @topicId) + url = DiscussionUtil.urlFor('create_thread', @topicId) - DiscussionUtil.safeAjax - $elem: $(event.target) - $loading: $(event.target) if event - url: url - type: "POST" - dataType: 'json' - async: false # TODO when the rest of the stuff below is made to work properly.. - data: - title: title - body: body - tags: tags - anonymous: anonymous - auto_subscribe: follow - error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors")) - success: (response, textStatus) => - # TODO: Move this out of the callback, this makes it feel sluggish - thread = new Thread response['content'] - DiscussionUtil.clearFormErrors(@$(".new-post-form-errors")) - @$el.hide() - @$(".new-post-title").val("").attr("prev-text", "") - @$(".new-post-body textarea").val("").attr("prev-text", "") - @$(".new-post-tags").val("") - @$(".new-post-tags").importTags("") - @collection.add thread + DiscussionUtil.safeAjax + $elem: $(event.target) + $loading: $(event.target) if event + url: url + type: "POST" + dataType: 'json' + async: false # TODO when the rest of the stuff below is made to work properly.. + data: + title: title + body: body + tags: tags + anonymous: anonymous + auto_subscribe: follow + error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors")) + success: (response, textStatus) => + # TODO: Move this out of the callback, this makes it feel sluggish + thread = new Thread response['content'] + DiscussionUtil.clearFormErrors(@$(".new-post-form-errors")) + @$el.hide() + @$(".new-post-title").val("").attr("prev-text", "") + @$(".new-post-body textarea").val("").attr("prev-text", "") + @$(".new-post-tags").val("") + @$(".new-post-tags").importTags("") + @collection.add thread diff --git a/lms/static/coffee/src/discussion/views/new_post_view.coffee b/lms/static/coffee/src/discussion/views/new_post_view.coffee index 927aa76536..8969d25748 100644 --- a/lms/static/coffee/src/discussion/views/new_post_view.coffee +++ b/lms/static/coffee/src/discussion/views/new_post_view.coffee @@ -1,175 +1,176 @@ -class @NewPostView extends Backbone.View +if Backbone? + class @NewPostView extends Backbone.View - initialize: () -> - @dropdownButton = @$(".topic_dropdown_button") - @topicMenu = @$(".topic_menu_wrapper") + initialize: () -> + @dropdownButton = @$(".topic_dropdown_button") + @topicMenu = @$(".topic_menu_wrapper") - @menuOpen = @dropdownButton.hasClass('dropped') + @menuOpen = @dropdownButton.hasClass('dropped') - @topicId = @$(".topic").first().data("discussion_id") - @topicText = @getFullTopicName(@$(".topic").first()) + @topicId = @$(".topic").first().data("discussion_id") + @topicText = @getFullTopicName(@$(".topic").first()) - @maxNameWidth = 100 - @setSelectedTopic() + @maxNameWidth = 100 + @setSelectedTopic() - DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body" - @$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions() + DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body" + @$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions() - events: - "submit .new-post-form": "createPost" - "click .topic_dropdown_button": "toggleTopicDropdown" - "click .topic_menu_wrapper": "setTopic" - "click .topic_menu_search": "ignoreClick" + events: + "submit .new-post-form": "createPost" + "click .topic_dropdown_button": "toggleTopicDropdown" + "click .topic_menu_wrapper": "setTopic" + "click .topic_menu_search": "ignoreClick" - # Because we want the behavior that when the body is clicked the menu is - # closed, we need to ignore clicks in the search field and stop propagation. - # Without this, clicking the search field would also close the menu. - ignoreClick: (event) -> - event.stopPropagation() + # Because we want the behavior that when the body is clicked the menu is + # closed, we need to ignore clicks in the search field and stop propagation. + # Without this, clicking the search field would also close the menu. + ignoreClick: (event) -> + event.stopPropagation() - toggleTopicDropdown: (event) -> - event.stopPropagation() - if @menuOpen - @hideTopicDropdown() - else - @showTopicDropdown() + toggleTopicDropdown: (event) -> + event.stopPropagation() + if @menuOpen + @hideTopicDropdown() + else + @showTopicDropdown() - showTopicDropdown: () -> - @menuOpen = true - @dropdownButton.addClass('dropped') - @topicMenu.show() - $(".form-topic-drop-search-input").focus() + showTopicDropdown: () -> + @menuOpen = true + @dropdownButton.addClass('dropped') + @topicMenu.show() + $(".form-topic-drop-search-input").focus() - $("body").bind "keydown", @setActiveItem - $("body").bind "click", @hideTopicDropdown + $("body").bind "keydown", @setActiveItem + $("body").bind "click", @hideTopicDropdown - # Set here because 1) the window might get resized and things could - # change and 2) can't set in initialize because the button is hidden - @maxNameWidth = @dropdownButton.width() * 0.9 + # Set here because 1) the window might get resized and things could + # change and 2) can't set in initialize because the button is hidden + @maxNameWidth = @dropdownButton.width() * 0.9 - # Need a fat arrow because hideTopicDropdown is passed as a callback to bind - hideTopicDropdown: () => - @menuOpen = false - @dropdownButton.removeClass('dropped') - @topicMenu.hide() + # Need a fat arrow because hideTopicDropdown is passed as a callback to bind + hideTopicDropdown: () => + @menuOpen = false + @dropdownButton.removeClass('dropped') + @topicMenu.hide() - $("body").unbind "keydown", @setActiveItem - $("body").unbind "click", @hideTopicDropdown + $("body").unbind "keydown", @setActiveItem + $("body").unbind "click", @hideTopicDropdown - setTopic: (event) -> - $target = $(event.target) - if $target.data('discussion_id') - @topicText = $target.html() - @topicText = @getFullTopicName($target) - @topicId = $target.data('discussion_id') - @setSelectedTopic() + setTopic: (event) -> + $target = $(event.target) + if $target.data('discussion_id') + @topicText = $target.html() + @topicText = @getFullTopicName($target) + @topicId = $target.data('discussion_id') + @setSelectedTopic() - setSelectedTopic: -> - @dropdownButton.html(@fitName(@topicText) + ' ') + setSelectedTopic: -> + @dropdownButton.html(@fitName(@topicText) + ' ') - getFullTopicName: (topicElement) -> - name = topicElement.html() - topicElement.parents('ul').not('.topic_menu').each -> - name = $(this).siblings('a').html() + ' / ' + name - return name + getFullTopicName: (topicElement) -> + name = topicElement.html() + topicElement.parents('ul').not('.topic_menu').each -> + name = $(this).siblings('a').html() + ' / ' + name + return name - getNameWidth: (name) -> - test = $("
    ") - test.css - "font-size": @dropdownButton.css('font-size') - opacity: 0 - position: 'absolute' - left: -1000 - top: -1000 - $("body").append(test) - test.html(name) - width = test.width() - test.remove() - return width + getNameWidth: (name) -> + test = $("
    ") + test.css + "font-size": @dropdownButton.css('font-size') + opacity: 0 + position: 'absolute' + left: -1000 + top: -1000 + $("body").append(test) + test.html(name) + width = test.width() + test.remove() + return width - fitName: (name) -> - width = @getNameWidth(name) - if width < @maxNameWidth - return name - path = (x.replace /^\s+|\s+$/g, "" for x in name.split("/")) - while path.length > 1 - path.shift() - partialName = "... / " + path.join(" / ") - if @getNameWidth(partialName) < @maxNameWidth - return partialName + fitName: (name) -> + width = @getNameWidth(name) + if width < @maxNameWidth + return name + path = (x.replace /^\s+|\s+$/g, "" for x in name.split("/")) + while path.length > 1 + path.shift() + partialName = "... / " + path.join(" / ") + if @getNameWidth(partialName) < @maxNameWidth + return partialName - rawName = path[0] + rawName = path[0] - name = "... / " + rawName + name = "... / " + rawName - while @getNameWidth(name) > @maxNameWidth - rawName = rawName[0...rawName.length-1] - name = "... / " + rawName + " ..." + while @getNameWidth(name) > @maxNameWidth + rawName = rawName[0...rawName.length-1] + name = "... / " + rawName + " ..." - return name + return name - createPost: (event) -> - event.preventDefault() - title = @$(".new-post-title").val() - body = @$(".new-post-body").find(".wmd-input").val() - tags = @$(".new-post-tags").val() + createPost: (event) -> + event.preventDefault() + title = @$(".new-post-title").val() + body = @$(".new-post-body").find(".wmd-input").val() + tags = @$(".new-post-tags").val() - anonymous = false || @$("input.discussion-anonymous").is(":checked") - follow = false || @$("input.discussion-follow").is(":checked") + anonymous = false || @$("input.discussion-anonymous").is(":checked") + follow = false || @$("input.discussion-follow").is(":checked") - $formTopicDropBtn.bind('click', showFormTopicDrop) - $formTopicDropMenu.bind('click', setFormTopic) + $formTopicDropBtn.bind('click', showFormTopicDrop) + $formTopicDropMenu.bind('click', setFormTopic) - url = DiscussionUtil.urlFor('create_thread', @topicId) + url = DiscussionUtil.urlFor('create_thread', @topicId) - DiscussionUtil.safeAjax - $elem: $(event.target) - $loading: $(event.target) if event - url: url - type: "POST" - dataType: 'json' - async: false # TODO when the rest of the stuff below is made to work properly.. - data: - title: title - body: body - tags: tags - anonymous: anonymous - auto_subscribe: follow - error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors")) - success: (response, textStatus) => - # TODO: Move this out of the callback, this makes it feel sluggish - thread = new Thread response['content'] - DiscussionUtil.clearFormErrors(@$(".new-post-form-errors")) - @$el.hide() - @$(".new-post-title").val("").attr("prev-text", "") - @$(".new-post-body textarea").val("").attr("prev-text", "") - @$(".new-post-tags").val("") - @$(".new-post-tags").importTags("") - @collection.add thread + DiscussionUtil.safeAjax + $elem: $(event.target) + $loading: $(event.target) if event + url: url + type: "POST" + dataType: 'json' + async: false # TODO when the rest of the stuff below is made to work properly.. + data: + title: title + body: body + tags: tags + anonymous: anonymous + auto_subscribe: follow + error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors")) + success: (response, textStatus) => + # TODO: Move this out of the callback, this makes it feel sluggish + thread = new Thread response['content'] + DiscussionUtil.clearFormErrors(@$(".new-post-form-errors")) + @$el.hide() + @$(".new-post-title").val("").attr("prev-text", "") + @$(".new-post-body textarea").val("").attr("prev-text", "") + @$(".new-post-tags").val("") + @$(".new-post-tags").importTags("") + @collection.add thread - setActiveItem: (event) -> - if event.which == 13 - $(".topic_menu_wrapper .focused").click() - return - if event.which != 40 && event.which != 38 - return - event.preventDefault() + setActiveItem: (event) -> + if event.which == 13 + $(".topic_menu_wrapper .focused").click() + return + if event.which != 40 && event.which != 38 + return + event.preventDefault() - items = $.makeArray($(".topic_menu_wrapper a").not(".hidden")) - index = items.indexOf($('.topic_menu_wrapper .focused')[0]) + items = $.makeArray($(".topic_menu_wrapper a").not(".hidden")) + index = items.indexOf($('.topic_menu_wrapper .focused')[0]) - if event.which == 40 - index = Math.min(index + 1, items.length - 1) - if event.which == 38 - index = Math.max(index - 1, 0) + if event.which == 40 + index = Math.min(index + 1, items.length - 1) + if event.which == 38 + index = Math.max(index - 1, 0) - $(".topic_menu_wrapper .focused").removeClass("focused") - $(items[index]).addClass("focused") + $(".topic_menu_wrapper .focused").removeClass("focused") + $(items[index]).addClass("focused") - itemTop = $(items[index]).parent().offset().top - scrollTop = $(".topic_menu").scrollTop() - itemFromTop = $(".topic_menu").offset().top - itemTop - scrollTarget = Math.min(scrollTop - itemFromTop, scrollTop) - scrollTarget = Math.max(scrollTop - itemFromTop - $(".topic_menu").height() + $(items[index]).height() + 20, scrollTarget) - $(".topic_menu").scrollTop(scrollTarget) + itemTop = $(items[index]).parent().offset().top + scrollTop = $(".topic_menu").scrollTop() + itemFromTop = $(".topic_menu").offset().top - itemTop + scrollTarget = Math.min(scrollTop - itemFromTop, scrollTop) + scrollTarget = Math.max(scrollTop - itemFromTop - $(".topic_menu").height() + $(items[index]).height() + 20, scrollTarget) + $(".topic_menu").scrollTop(scrollTarget) diff --git a/lms/static/coffee/src/discussion/views/response_comment_view.coffee b/lms/static/coffee/src/discussion/views/response_comment_view.coffee index 262617eb84..5fff33e6bb 100644 --- a/lms/static/coffee/src/discussion/views/response_comment_view.coffee +++ b/lms/static/coffee/src/discussion/views/response_comment_view.coffee @@ -1,23 +1,24 @@ -class @ResponseCommentView extends DiscussionContentView - tagName: "li" - template: _.template($("#response-comment-template").html()) - initLocal: -> - # TODO .response-local is the parent of the comments so @$local is null, not sure what was intended here... - @$local = @$el.find(".response-local") - @$delegateElement = @$local +if Backbone? + class @ResponseCommentView extends DiscussionContentView + tagName: "li" + template: _.template($("#response-comment-template").html()) + initLocal: -> + # TODO .response-local is the parent of the comments so @$local is null, not sure what was intended here... + @$local = @$el.find(".response-local") + @$delegateElement = @$local - render: -> - @$el.html(@template(@model.toJSON())) - @initLocal() - @delegateEvents() - @renderAttrs() - @$el.find(".timeago").timeago() - @convertMath() - @ - convertMath: -> - body = @$el.find(".response-body") - body.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight body.html() - # This removes paragraphs so that comments are more compact - body.children("p").each (index, elem) -> - $(elem).replaceWith($(elem).html()) - MathJax.Hub.Queue ["Typeset", MathJax.Hub, body[0]] + render: -> + @$el.html(@template(@model.toJSON())) + @initLocal() + @delegateEvents() + @renderAttrs() + @$el.find(".timeago").timeago() + @convertMath() + @ + convertMath: -> + body = @$el.find(".response-body") + body.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight body.html() + # This removes paragraphs so that comments are more compact + body.children("p").each (index, elem) -> + $(elem).replaceWith($(elem).html()) + MathJax.Hub.Queue ["Typeset", MathJax.Hub, body[0]] diff --git a/lms/static/coffee/src/discussion/views/thread_response_view.coffee b/lms/static/coffee/src/discussion/views/thread_response_view.coffee index 86deb82e6d..dac30401d8 100644 --- a/lms/static/coffee/src/discussion/views/thread_response_view.coffee +++ b/lms/static/coffee/src/discussion/views/thread_response_view.coffee @@ -1,117 +1,118 @@ -class @ThreadResponseView extends DiscussionContentView - tagName: "li" - template: _.template($("#thread-response-template").html()) +if Backbone? + class @ThreadResponseView extends DiscussionContentView + tagName: "li" + template: _.template($("#thread-response-template").html()) - events: - "click .vote-btn": "toggleVote" - "submit .comment-form": "submitComment" - "click .action-endorse": "toggleEndorse" - "click .action-delete": "delete" + events: + "click .vote-btn": "toggleVote" + "submit .comment-form": "submitComment" + "click .action-endorse": "toggleEndorse" + "click .action-delete": "delete" - render: -> - @$el.html(@template(@model.toJSON())) - @initLocal() - @delegateEvents() - if window.user.voted(@model) - @$(".vote-btn").addClass("is-cast") - @renderAttrs() - @$el.find(".posted-details").timeago() - @convertMath() - @renderComments() - @ + render: -> + @$el.html(@template(@model.toJSON())) + @initLocal() + @delegateEvents() + if window.user.voted(@model) + @$(".vote-btn").addClass("is-cast") + @renderAttrs() + @$el.find(".posted-details").timeago() + @convertMath() + @renderComments() + @ - convertMath: -> - element = @$(".response-body") - element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.html() - MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] + convertMath: -> + element = @$(".response-body") + element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.html() + MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] - renderComments: -> - @model.get("comments").each @renderComment + renderComments: -> + @model.get("comments").each @renderComment - renderComment: (comment) => - comment.set('thread', @model.get('thread')) - view = new ResponseCommentView(model: comment) - view.render() - @$el.find(".comments li:last").before(view.el) + renderComment: (comment) => + comment.set('thread', @model.get('thread')) + view = new ResponseCommentView(model: comment) + view.render() + @$el.find(".comments li:last").before(view.el) - toggleVote: (event) -> - event.preventDefault() - @$(".vote-btn").toggleClass("is-cast") - if @$(".vote-btn").hasClass("is-cast") - @vote() - else - @unvote() + toggleVote: (event) -> + event.preventDefault() + @$(".vote-btn").toggleClass("is-cast") + if @$(".vote-btn").hasClass("is-cast") + @vote() + else + @unvote() - vote: -> - url = @model.urlFor("upvote") - @$(".votes-count-number").html(parseInt(@$(".votes-count-number").html()) + 1) - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) + vote: -> + url = @model.urlFor("upvote") + @$(".votes-count-number").html(parseInt(@$(".votes-count-number").html()) + 1) + DiscussionUtil.safeAjax + $elem: @$(".discussion-vote") + url: url + type: "POST" + success: (response, textStatus) => + if textStatus == 'success' + @model.set(response) - unvote: -> - url = @model.urlFor("unvote") - @$(".votes-count-number").html(parseInt(@$(".votes-count-number").html()) - 1) - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) + unvote: -> + url = @model.urlFor("unvote") + @$(".votes-count-number").html(parseInt(@$(".votes-count-number").html()) - 1) + DiscussionUtil.safeAjax + $elem: @$(".discussion-vote") + url: url + type: "POST" + success: (response, textStatus) => + if textStatus == 'success' + @model.set(response) - submitComment: (event) -> - event.preventDefault() - url = @model.urlFor('reply') - body = @$(".comment-form-input").val() - if not body.trim().length - return - comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"), user_id: window.user.get("id")) - @renderComment(comment) - @trigger "comment:add", comment - @$(".comment-form-input").val("") + submitComment: (event) -> + event.preventDefault() + url = @model.urlFor('reply') + body = @$(".comment-form-input").val() + if not body.trim().length + return + comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"), user_id: window.user.get("id")) + @renderComment(comment) + @trigger "comment:add", comment + @$(".comment-form-input").val("") - DiscussionUtil.safeAjax - $elem: $(event.target) - url: url - type: "POST" - dataType: 'json' - data: - body: body + DiscussionUtil.safeAjax + $elem: $(event.target) + url: url + type: "POST" + dataType: 'json' + data: + body: body - delete: (event) -> - event.preventDefault() - if not @model.can('can_delete') - return - console.log $(event.target) - url = @model.urlFor('delete') - if not confirm "Are you sure to delete this response? " - return - @model.remove() - @$el.remove() - $elem = $(event.target) - DiscussionUtil.safeAjax - $elem: $elem - url: url - type: "POST" - success: (response, textStatus) => + delete: (event) -> + event.preventDefault() + if not @model.can('can_delete') + return + console.log $(event.target) + url = @model.urlFor('delete') + if not confirm "Are you sure to delete this response? " + return + @model.remove() + @$el.remove() + $elem = $(event.target) + DiscussionUtil.safeAjax + $elem: $elem + url: url + type: "POST" + success: (response, textStatus) => - toggleEndorse: (event) -> - event.preventDefault() - if not @model.can('can_endorse') - return - $elem = $(event.target) - url = @model.urlFor('endorse') - endorsed = @model.get('endorsed') - data = { endorsed: not endorsed } - @model.set('endorsed', not endorsed) - @trigger "comment:endorse", not endorsed - DiscussionUtil.safeAjax - $elem: $elem - url: url - data: data - type: "POST" + toggleEndorse: (event) -> + event.preventDefault() + if not @model.can('can_endorse') + return + $elem = $(event.target) + url = @model.urlFor('endorse') + endorsed = @model.get('endorsed') + data = { endorsed: not endorsed } + @model.set('endorsed', not endorsed) + @trigger "comment:endorse", not endorsed + DiscussionUtil.safeAjax + $elem: $elem + url: url + data: data + type: "POST"