Commit 952c9d56 by Rocky Duan

moved some stuff

parent ee217518
class @Content extends Backbone.Model
template: -> DiscussionUtil.getTemplate('_content')
actions:
editable: '.admin-edit'
can_reply: '.discussion-reply'
can_endorse: '.admin-endorse'
can_delete: '.admin-delete'
can_openclose: '.admin-openclose'
urlMappers: {}
urlFor: (name) ->
@urlMappers[name].apply(@)
can: (action) ->
DiscussionUtil.getContentInfo @id, action
updateInfo: (info) ->
@set('ability', info.ability)
@set('voted', info.voted)
@set('subscribed', info.subscribed)
addComment: (comment, options) ->
options ||= {}
if not options.silent
thread = @get('thread')
comments_count = parseInt(thread.get('comments_count'))
thread.set('comments_count', comments_count + 1)
@get('children').push comment
model = new Comment $.extend {}, comment, { thread: @get('thread') }
@get('comments').add model
model
removeComment: (comment) ->
thread = @get('thread')
comments_count = parseInt(thread.get('comments_count'))
thread.set('comments_count', comments_count - 1 - comment.getCommentsCount())
resetComments: (children) ->
@set 'children', []
@set 'comments', new Comments()
for comment in (children || [])
@addComment comment, { silent: true }
initialize: ->
DiscussionUtil.addContent @id, @
@resetComments(@get('children'))
class @ContentView extends Backbone.View
$: (selector) ->
@$local.find(selector)
partial:
endorsed: (endorsed) ->
if endorsed
@$el.addClass("endorsed")
else
@$el.removeClass("endorsed")
closed: (closed) -> # we should just re-render the whole thread, or update according to new abilities
if closed
@$el.addClass("closed")
@$(".admin-openclose").text "Re-open Thread"
else
@$el.removeClass("closed")
@$(".admin-openclose").text "Close Thread"
voted: (voted) ->
@$(".discussion-vote-up").removeClass("voted") if voted != "up"
@$(".discussion-vote-down").removeClass("voted") if voted != "down"
@$(".discussion-vote-#{voted}").addClass("voted") if voted in ["up", "down"]
votes_point: (votes_point) ->
@$(".discussion-votes-point").html(votes_point)
comments_count: (comments_count) ->
@$(".comments-count").html(comments_count)
subscribed: (subscribed) ->
if subscribed
@$(".discussion-follow-thread").addClass("discussion-unfollow-thread").html("Unfollow")
else
@$(".discussion-follow-thread").removeClass("discussion-unfollow-thread").html("Follow")
ability: (ability) ->
for action, elemSelector of @model.actions
if not ability[action]
@$(elemSelector).parent().remove()
$discussionContent: ->
@_discussionContent ||= @$el.children(".discussion-content")
$showComments: ->
@_showComments ||= @$(".discussion-show-comments")
updateShowComments: ->
if @showed
@$showComments().html @$showComments().html().replace "Show", "Hide"
else
@$showComments().html @$showComments().html().replace "Hide", "Show"
retrieved: ->
@$showComments().hasClass("retrieved")
hideSingleThread: (event) ->
@$el.children(".comments").hide()
@showed = false
@updateShowComments()
showSingleThread: (event) ->
if @retrieved()
@$el.children(".comments").show()
@showed = true
@updateShowComments()
else
$elem = $.merge @$(".thread-title"), @$showComments()
url = @model.urlFor('retrieve')
DiscussionUtil.get $elem, url, {}, (response, textStatus) =>
@showed = true
@updateShowComments()
@$showComments().addClass("retrieved")
@$el.children(".comments").replaceWith response.html
@model.resetComments response.content.children
@initCommentViews()
DiscussionUtil.bulkUpdateContentInfo response.annotated_content_info
toggleSingleThread: (event) ->
if @showed
@hideSingleThread(event)
else
@showSingleThread(event)
initCommentViews: ->
@$el.children(".comments").children(".comment").each (index, elem) =>
model = @model.get('comments').find $(elem).attr("_id")
if not model.view
commentView = new CommentView el: elem, model: model
reply: ->
if @model.get('type') == 'thread'
@showSingleThread()
$replyView = @$(".discussion-reply-new")
if $replyView.length
$replyView.show()
else
view = {}
view.id = @model.id
view.showWatchCheckbox = not @model.get('thread').get('subscribed')
html = Mustache.render DiscussionUtil.getTemplate('_reply'), view
@$discussionContent().append html
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "reply-body"
@$(".discussion-submit-post").click $.proxy(@submitReply, @)
@$(".discussion-cancel-post").click $.proxy(@cancelReply, @)
@$(".discussion-reply").hide()
@$(".discussion-edit").hide()
submitReply: (event) ->
url = @model.urlFor('reply')
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "reply-body"
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
autowatch = false || @$(".discussion-auto-watch").is(":checked")
DiscussionUtil.safeAjax
$elem: $(event.target)
url: url
type: "POST"
dataType: 'json'
data:
body: body
anonymous: anonymous
auto_subscribe: autowatch
error: DiscussionUtil.formErrorHandler @$(".discussion-errors")
success: (response, textStatus) =>
DiscussionUtil.clearFormErrors @$(".discussion-errors")
$comment = $(response.html)
@$el.children(".comments").prepend $comment
DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "reply-body", ""
comment = @model.addComment response.content
commentView = new CommentView el: $comment[0], model: comment
comment.updateInfo response.annotated_content_info
@cancelReply()
cancelReply: ->
$replyView = @$(".discussion-reply-new")
if $replyView.length
$replyView.hide()
@$(".discussion-reply").show()
@$(".discussion-edit").show()
unvote: (event) ->
url = @model.urlFor('unvote')
$elem = @$(".discussion-vote")
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
@model.set('voted', '')
@model.set('votes_point', response.votes.point)
vote: (event, value) ->
url = @model.urlFor("#{value}vote")
$elem = @$(".discussion-vote")
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
@model.set('voted', value)
@model.set('votes_point', response.votes.point)
toggleVote: (event) ->
$elem = $(event.target)
value = $elem.attr("value")
if @model.get("voted") == value
@unvote(event)
else
@vote(event, value)
toggleEndorse: (event) ->
$elem = $(event.target)
url = @model.urlFor('endorse')
endorsed = @model.get('endorsed')
data = { endorsed: not endorsed }
DiscussionUtil.post $elem, url, data, (response, textStatus) =>
@model.set('endorsed', not endorsed)
toggleFollow: (event) ->
$elem = $(event.target)
subscribed = @model.get('subscribed')
if subscribed
url = @model.urlFor('unfollow')
else
url = @model.urlFor('follow')
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
@model.set('subscribed', not subscribed)
toggleClosed: (event) ->
$elem = $(event.target)
url = @model.urlFor('close')
closed = @model.get('closed')
data = { closed: not closed }
DiscussionUtil.post $elem, url, data, (response, textStatus) =>
@model.set('closed', not closed)
edit: (event) ->
@$(".discussion-content-wrapper").hide()
$editView = @$(".discussion-content-edit")
if $editView.length
$editView.show()
else
view = {}
view.id = @model.id
if @model.get('type') == 'thread'
view.title = @$(".thread-raw-title").html()
view.body = @$(".thread-raw-body").html()
view.tags = @$(".thread-raw-tags").html()
else
view.body = @$(".comment-raw-body").html()
@$discussionContent().append Mustache.render DiscussionUtil.getTemplate("_edit_#{@model.get('type')}"), view
Discussion.makeWmdEditor @$el, $.proxy(@$, @), "#{@model.get('type')}-body-edit"
@$(".thread-tags-edit").tagsInput DiscussionUtil.tagsInputOptions()
@$(".discussion-submit-update").unbind("click").click $.proxy(@submitEdit, @)
@$(".discussion-cancel-update").unbind("click").click $.proxy(@cancelEdit, @)
submitEdit: (event) ->
url = @model.urlFor('update')
data = {}
if @model.get('type') == 'thread'
data.title = @$(".thread-title-edit").val()
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "thread-body-edit"
data.tags = @$(".thread-tags-edit").val()
else
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "comment-body-edit"
DiscussionUtil.safeAjax
$elem: $(event.target)
url: url
type: "POST"
dataType: 'json'
data: data
error: DiscussionUtil.formErrorHandler @$(".discussion-update-errors")
success: (response, textStatus) =>
DiscussionUtil.clearFormErrors @$(".discussion-update-errors")
@$discussionContent().replaceWith(response.html)
@model.set response.content
@model.updateInfo response.annotated_content_info
cancelEdit: (event) ->
@$(".discussion-content-edit").hide()
@$(".discussion-content-wrapper").show()
delete: (event) ->
url = @model.urlFor('delete')
if @model.get('type') == 'thread'
c = confirm "Are you sure to delete thread \"#{@model.get('title')}\"?"
else
c = confirm "Are you sure to delete this comment? "
if not c
return
$elem = $(event.target)
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
@$el.remove()
@model.get('thread').removeComment(@model)
events:
"click .discussion-follow-thread": "toggleFollow"
"click .thread-title": "toggleSingleThread"
"click .discussion-show-comments": "toggleSingleThread"
"click .discussion-reply-thread": "reply"
"click .discussion-reply-comment": "reply"
"click .discussion-cancel-reply": "cancelReply"
"click .discussion-vote-up": "toggleVote"
"click .discussion-vote-down": "toggleVote"
"click .admin-endorse": "toggleEndorse"
"click .admin-openclose": "toggleClosed"
"click .admin-edit": "edit"
"click .admin-delete": "delete"
initLocal: ->
@$local = @$el.children(".local")
@$delegateElement = @$local
initTitle: ->
$contentTitle = @$(".thread-title")
if $contentTitle.length
$contentTitle.html DiscussionUtil.unescapeHighlightTag DiscussionUtil.stripLatexHighlight $contentTitle.html()
initBody: ->
$contentBody = @$(".content-body")
$contentBody.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight $contentBody.html()
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
initTimeago: ->
@$("span.timeago").timeago()
initPermalink: ->
@$(".discussion-permanent-link").attr "href", @model.permalink()
renderPartial: ->
for attr, value of @model.changedAttributes()
if @partial[attr]
@partial[attr].apply(@, [value])
initBindings: ->
@model.view = @
@model.bind('change', @renderPartial, @)
initialize: ->
@initBindings()
@initLocal()
@initTimeago()
@initTitle()
@initBody()
@initCommentViews()
class @Thread extends @Content
urlMappers:
'retrieve' : -> DiscussionUtil.urlFor('retrieve_single_thread', @discussion.id, @id)
'reply' : -> DiscussionUtil.urlFor('create_comment', @id)
'unvote' : -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
'upvote' : -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
'downvote' : -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
'close' : -> DiscussionUtil.urlFor('openclose_thread', @id)
'update' : -> DiscussionUtil.urlFor('update_thread', @id)
'delete' : -> DiscussionUtil.urlFor('delete_thread', @id)
'follow' : -> DiscussionUtil.urlFor('follow_thread', @id)
'unfollow' : -> DiscussionUtil.urlFor('unfollow_thread', @id)
initialize: ->
@set('thread', @)
super()
permalink: ->
discussion_id = @get('commentable_id')
return Discussion.urlFor("permanent_link_thread", discussion_id, @id)
class @ThreadView extends @ContentView
class @Comment extends @Content
urlMappers:
'reply': -> DiscussionUtil.urlFor('create_sub_comment', @id)
'unvote': -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
'upvote': -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
'downvote': -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
'endorse': -> DiscussionUtil.urlFor('endorse_comment', @id)
'update': -> DiscussionUtil.urlFor('update_comment', @id)
'delete': -> DiscussionUtil.urlFor('delete_comment', @id)
permalink: ->
thread_id = @get('thread').id
discussion_id = @get('thread').get('commentable_id')
return Discussion.urlFor("permanent_link_comment", discussion_id, thread_id, @id)
getCommentsCount: ->
count = 0
@get('comments').each (comment) ->
count += comment.getCommentsCount() + 1
count
class @CommentView extends @ContentView
class @Comments extends Backbone.Collection
model: Comment
initialize: ->
@bind "add", (item) =>
item.collection = @
find: (id) ->
_.first @where(id: id)
class @Discussion extends Backbone.Collection
model: Thread
initialize: ->
DiscussionUtil.addDiscussion @id, @
@bind "add", (item) =>
item.discussion = @
find: (id) ->
_.first @where(id: id)
addThread: (thread, options) ->
options ||= {}
model = new Thread thread
@add model
model
class @DiscussionView extends Backbone.View
$: (selector) ->
@$local.find(selector)
initLocal: ->
@$local = @$el.children(".local")
@$delegateElement = @$local
initialize: ->
@initLocal()
@model.id = @$el.attr("_id")
@model.view = @
@$el.children(".threads").children(".thread").each (index, elem) =>
threadView = new ThreadView el: elem, model: @model.find $(elem).attr("_id")
if @$el.hasClass("forum-discussion")
$(".discussion-sidebar").find(".sidebar-new-post-button")
.unbind('click').click $.proxy @newPost, @
else if @$el.hasClass("inline-discussion")
@newPost()
reload: ($elem, url) ->
if not url then return
DiscussionUtil.get $elem, url, {}, (response, textStatus) =>
$parent = @$el.parent()
@$el.replaceWith(response.html)
$discussion = $parent.find("section.discussion")
@model.reset(response.discussionData, { silent: false })
view = new DiscussionView el: $discussion[0], model: @model
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
loadSimilarPost: (event) ->
$title = @$(".new-post-title")
$wrapper = @$(".new-post-similar-posts-wrapper")
$similarPosts = @$(".new-post-similar-posts")
prevText = $title.attr("prev-text")
text = $title.val()
if text == prevText
if @$(".similar-post").length
$wrapper.show()
else if $.trim(text).length
$elem = $(event.target)
url = DiscussionUtil.urlFor 'search_similar_threads', @model.id
data = { text: @$(".new-post-title").val() }
DiscussionUtil.get $elem, url, data, (response, textStatus) =>
$similarPosts.empty()
if $.type(response) == "array" and response.length
$wrapper.show()
for thread in response
$similarPost = $("<a>").addClass("similar-post")
.html(thread["title"])
.attr("href", "javascript:void(0)") #TODO
.appendTo($similarPosts)
else
$wrapper.hide()
else
$wrapper.hide()
$title.attr("prev-text", text)
newPost: ->
if not @$(".wmd-panel").length
view = { discussion_id: @model.id }
@$el.children(".discussion-non-content").append Mustache.render DiscussionUtil.getTemplate("_new_post"), view
$newPostBody = @$(".new-post-body")
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body"
$input = DiscussionUtil.getWmdInput @$el, $.proxy(@$, @), "new-post-body"
$input.attr("placeholder", "post a new topic...")
if @$el.hasClass("inline-discussion")
$input.bind 'focus', (e) =>
@$(".new-post-form").removeClass('collapsed')
else if @$el.hasClass("forum-discussion")
@$(".new-post-form").removeClass('collapsed')
@$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions()
@$(".new-post-title").blur $.proxy(@loadSimilarPost, @)
@$(".hide-similar-posts").click =>
@$(".new-post-similar-posts-wrapper").hide()
@$(".discussion-submit-post").click $.proxy(@submitNewPost, @)
@$(".discussion-cancel-post").click $.proxy(@cancelNewPost, @)
@$(".new-post-form").show()
submitNewPost: (event) ->
title = @$(".new-post-title").val()
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "new-post-body"
tags = @$(".new-post-tags").val()
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
autowatch = false || @$(".discussion-auto-watch").is(":checked")
url = DiscussionUtil.urlFor('create_thread', @model.id)
DiscussionUtil.safeAjax
$elem: $(event.target)
url: url
type: "POST"
dataType: 'json'
data:
title: title
body: body
tags: tags
anonymous: anonymous
auto_subscribe: autowatch
error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors"))
success: (response, textStatus) =>
DiscussionUtil.clearFormErrors(@$(".new-post-form-errors"))
$thread = $(response.html)
@$el.children(".threads").prepend($thread)
@$(".new-post-title").val("")
DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "new-post-body", ""
@$(".new-post-tags").val("")
@$(".new-post-tags").importTags("")
thread = @model.addThread response.content
threadView = new ThreadView el: $thread[0], model: thread
thread.updateInfo response.annotated_content_info
@cancelNewPost()
cancelNewPost: (event) ->
if @$el.hasClass("inline-discussion")
@$(".new-post-form").addClass("collapsed")
else if @$el.hasClass("forum-discussion")
@$(".new-post-form").hide()
search: (event) ->
event.preventDefault()
$elem = $(event.target)
url = URI($elem.attr("action")).addSearch({text: @$(".search-input").val()})
@reload($elem, url)
sort: ->
$elem = $(event.target)
url = $elem.attr("sort-url")
@reload($elem, url)
page: (event) ->
$elem = $(event.target)
url = $elem.attr("page-url")
@reload($elem, url)
events:
"submit .search-wrapper>.discussion-search-form": "search"
"click .discussion-search-link": "search"
"click .discussion-sort-link": "sort"
"click .discussion-page-link": "page"
class @DiscussionModuleView extends Backbone.View
events:
"click .discussion-show": "toggleDiscussion"
toggleDiscussion: (event) ->
if @showed
@$("section.discussion").hide()
$(event.target).html("Show Discussion")
@showed = false
else
if @retrieved
@$("section.discussion").show()
$(event.target).html("Hide Discussion")
@showed = true
else
$elem = $(event.target)
discussion_id = $elem.attr("discussion_id")
url = DiscussionUtil.urlFor 'retrieve_discussion', discussion_id
Discussion.safeAjax
$elem: $elem
url: url
type: "GET"
dataType: 'json'
success: (response, textStatus) =>
@$el.append(response.html)
$discussion = @$el.find("section.discussion")
$(event.target).html("Hide Discussion")
discussion = new Discussion()
discussion.reset(response.discussionData, {silent: false})
view = new DiscussionView(el: $discussion[0], model: discussion)
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
@retrieved = true
@showed = true
$ ->
window.$$contents = {}
window.$$discussions = {}
$(".discussion-module").each (index, elem) ->
view = new DiscussionModuleView(el: elem)
$("section.discussion").each (index, elem) ->
discussionData = DiscussionUtil.getDiscussionData($(elem).attr("_id"))
discussion = new Discussion()
discussion.reset(discussionData, {silent: false})
view = new DiscussionView(el: elem, model: discussion)
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
if not @Discussion? class @Content extends Backbone.Model
@Discussion = {}
Discussion = @Discussion template: -> DiscussionUtil.getTemplate('_content')
initializeVote = (content) -> actions:
$content = $(content) editable: '.admin-edit'
$local = Discussion.generateLocal($content.children(".discussion-content")) can_reply: '.discussion-reply'
id = $content.attr("_id") can_endorse: '.admin-endorse'
if Discussion.isUpvoted id can_delete: '.admin-delete'
$local(".discussion-vote-up").addClass("voted") can_openclose: '.admin-openclose'
else if Discussion.isDownvoted id
$local(".discussion-vote-down").addClass("voted")
initializeFollowThread = (thread) -> urlMappers: {}
$thread = $(thread)
id = $thread.attr("_id")
$thread.children(".discussion-content")
.find(".follow-wrapper")
.append(Discussion.subscriptionLink('thread', id))
@Discussion = $.extend @Discussion, urlFor: (name) ->
@urlMappers[name].apply(@)
bindContentEvents: (content) -> can: (action) ->
DiscussionUtil.getContentInfo @id, action
$content = $(content) updateInfo: (info) ->
$discussionContent = $content.children(".discussion-content") @set('ability', info.ability)
$local = Discussion.generateLocal($discussionContent) @set('voted', info.voted)
@set('subscribed', info.subscribed)
id = $content.attr("_id") addComment: (comment, options) ->
options ||= {}
if not options.silent
thread = @get('thread')
comments_count = parseInt(thread.get('comments_count'))
thread.set('comments_count', comments_count + 1)
@get('children').push comment
model = new Comment $.extend {}, comment, { thread: @get('thread') }
@get('comments').add model
model
handleReply = (elem) -> removeComment: (comment) ->
$replyView = $local(".discussion-reply-new") thread = @get('thread')
if $replyView.length comments_count = parseInt(thread.get('comments_count'))
$replyView.show() thread.set('comments_count', comments_count - 1 - comment.getCommentsCount())
resetComments: (children) ->
@set 'children', []
@set 'comments', new Comments()
for comment in (children || [])
@addComment comment, { silent: true }
initialize: ->
DiscussionUtil.addContent @id, @
@resetComments(@get('children'))
class @ContentView extends Backbone.View
$: (selector) ->
@$local.find(selector)
partial:
endorsed: (endorsed) ->
if endorsed
@$el.addClass("endorsed")
else else
thread_id = $discussionContent.parents(".thread").attr("_id") @$el.removeClass("endorsed")
view =
id: id closed: (closed) -> # we should just re-render the whole thread, or update according to new abilities
showWatchCheckbox: not Discussion.isSubscribed(thread_id, "thread") if closed
$discussionContent.append Mustache.render Discussion.replyTemplate, view @$el.addClass("closed")
Discussion.makeWmdEditor $content, $local, "reply-body" @$(".admin-openclose").text "Re-open Thread"
$local(".discussion-submit-post").click -> handleSubmitReply(this)
$local(".discussion-cancel-post").click -> handleCancelReply(this)
$local(".discussion-reply").hide()
$local(".discussion-edit").hide()
handleCancelReply = (elem) ->
$replyView = $local(".discussion-reply-new")
if $replyView.length
$replyView.hide()
$local(".discussion-reply").show()
$local(".discussion-edit").show()
handleSubmitReply = (elem) ->
if $content.hasClass("thread")
url = Discussion.urlFor('create_comment', id)
else if $content.hasClass("comment")
url = Discussion.urlFor('create_sub_comment', id)
else else
return @$el.removeClass("closed")
@$(".admin-openclose").text "Close Thread"
voted: (voted) ->
@$(".discussion-vote-up").removeClass("voted") if voted != "up"
@$(".discussion-vote-down").removeClass("voted") if voted != "down"
@$(".discussion-vote-#{voted}").addClass("voted") if voted in ["up", "down"]
votes_point: (votes_point) ->
@$(".discussion-votes-point").html(votes_point)
comments_count: (comments_count) ->
@$(".comments-count").html(comments_count)
subscribed: (subscribed) ->
if subscribed
@$(".discussion-follow-thread").addClass("discussion-unfollow-thread").html("Unfollow")
else
@$(".discussion-follow-thread").removeClass("discussion-unfollow-thread").html("Follow")
ability: (ability) ->
for action, elemSelector of @model.actions
if not ability[action]
@$(elemSelector).parent().remove()
$discussionContent: ->
@_discussionContent ||= @$el.children(".discussion-content")
$showComments: ->
@_showComments ||= @$(".discussion-show-comments")
updateShowComments: ->
if @showed
@$showComments().html @$showComments().html().replace "Show", "Hide"
else
@$showComments().html @$showComments().html().replace "Hide", "Show"
body = Discussion.getWmdContent $content, $local, "reply-body" retrieved: ->
@$showComments().hasClass("retrieved")
anonymous = false || $local(".discussion-post-anonymously").is(":checked") hideSingleThread: (event) ->
autowatch = false || $local(".discussion-auto-watch").is(":checked") @$el.children(".comments").hide()
@showed = false
@updateShowComments()
Discussion.safeAjax showSingleThread: (event) ->
$elem: $(elem) if @retrieved()
@$el.children(".comments").show()
@showed = true
@updateShowComments()
else
$elem = $.merge @$(".thread-title"), @$showComments()
url = @model.urlFor('retrieve')
DiscussionUtil.get $elem, url, {}, (response, textStatus) =>
@showed = true
@updateShowComments()
@$showComments().addClass("retrieved")
@$el.children(".comments").replaceWith response.html
@model.resetComments response.content.children
@initCommentViews()
DiscussionUtil.bulkUpdateContentInfo response.annotated_content_info
toggleSingleThread: (event) ->
if @showed
@hideSingleThread(event)
else
@showSingleThread(event)
initCommentViews: ->
@$el.children(".comments").children(".comment").each (index, elem) =>
model = @model.get('comments').find $(elem).attr("_id")
if not model.view
commentView = new CommentView el: elem, model: model
reply: ->
if @model.get('type') == 'thread'
@showSingleThread()
$replyView = @$(".discussion-reply-new")
if $replyView.length
$replyView.show()
else
view = {}
view.id = @model.id
view.showWatchCheckbox = not @model.get('thread').get('subscribed')
html = Mustache.render DiscussionUtil.getTemplate('_reply'), view
@$discussionContent().append html
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "reply-body"
@$(".discussion-submit-post").click $.proxy(@submitReply, @)
@$(".discussion-cancel-post").click $.proxy(@cancelReply, @)
@$(".discussion-reply").hide()
@$(".discussion-edit").hide()
submitReply: (event) ->
url = @model.urlFor('reply')
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "reply-body"
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
autowatch = false || @$(".discussion-auto-watch").is(":checked")
DiscussionUtil.safeAjax
$elem: $(event.target)
url: url url: url
type: "POST" type: "POST"
dataType: 'json' dataType: 'json'
data: data:
body: body body: body
anonymous: anonymous anonymous: anonymous
autowatch: autowatch auto_subscribe: autowatch
error: Discussion.formErrorHandler($local(".discussion-errors")) error: DiscussionUtil.formErrorHandler @$(".discussion-errors")
success: (response, textStatus) -> success: (response, textStatus) =>
Discussion.clearFormErrors($local(".discussion-errors")) DiscussionUtil.clearFormErrors @$(".discussion-errors")
$comment = $(response.html) $comment = $(response.html)
$content.children(".comments").prepend($comment) @$el.children(".comments").prepend $comment
Discussion.setWmdContent $content, $local, "reply-body", "" DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "reply-body", ""
Discussion.setContentInfo response.content['id'], 'can_reply', true comment = @model.addComment response.content
Discussion.setContentInfo response.content['id'], 'editable', true commentView = new CommentView el: $comment[0], model: comment
Discussion.extendContentInfo response.content['id'], response['annotated_content_info'] comment.updateInfo response.annotated_content_info
Discussion.initializeContent($comment)
Discussion.bindContentEvents($comment)
@cancelReply() @cancelReply()
$local(".discussion-reply-new").hide()
$local(".discussion-reply").show() cancelReply: ->
$local(".discussion-edit").show() $replyView = @$(".discussion-reply-new")
$discussionContent.attr("status", "normal") if $replyView.length
$replyView.hide()
handleVote = (elem, value) -> @$(".discussion-reply").show()
contentType = if $content.hasClass("thread") then "thread" else "comment" @$(".discussion-edit").show()
url = Discussion.urlFor("#{value}vote_#{contentType}", id)
Discussion.safeAjax unvote: (event) ->
$elem: $local(".discussion-vote") url = @model.urlFor('unvote')
url: url $elem = @$(".discussion-vote")
type: "POST" DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
dataType: "json" @model.set('voted', '')
success: (response, textStatus) -> @model.set('votes_point', response.votes.point)
if textStatus == "success"
$local(".discussion-vote").removeClass("voted") vote: (event, value) ->
$local(".discussion-vote-#{value}").addClass("voted") url = @model.urlFor("#{value}vote")
$local(".discussion-votes-point").html response.votes.point $elem = @$(".discussion-vote")
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
handleUnvote = (elem, value) -> @model.set('voted', value)
contentType = if $content.hasClass("thread") then "thread" else "comment" @model.set('votes_point', response.votes.point)
url = Discussion.urlFor("undo_vote_for_#{contentType}", id)
Discussion.safeAjax toggleVote: (event) ->
$elem: $local(".discussion-vote") $elem = $(event.target)
url: url value = $elem.attr("value")
type: "POST" if @model.get("voted") == value
dataType: "json" @unvote(event)
success: (response, textStatus) ->
if textStatus == "success"
$local(".discussion-vote").removeClass("voted")
$local(".discussion-votes-point").html response.votes.point
handleCancelEdit = (elem) ->
$local(".discussion-content-edit").hide()
$local(".discussion-content-wrapper").show()
handleEditThread = (elem) ->
$local(".discussion-content-wrapper").hide()
$editView = $local(".discussion-content-edit")
if $editView.length
$editView.show()
else else
view = { @vote(event, value)
id: id
title: $local(".thread-raw-title").html() toggleEndorse: (event) ->
body: $local(".thread-raw-body").html() $elem = $(event.target)
tags: $local(".thread-raw-tags").html() url = @model.urlFor('endorse')
} endorsed = @model.get('endorsed')
$discussionContent.append Mustache.render Discussion.editThreadTemplate, view data = { endorsed: not endorsed }
Discussion.makeWmdEditor $content, $local, "thread-body-edit" DiscussionUtil.post $elem, url, data, (response, textStatus) =>
$local(".thread-tags-edit").tagsInput Discussion.tagsInputOptions() @model.set('endorsed', not endorsed)
$local(".discussion-submit-update").unbind("click").click -> handleSubmitEditThread(this)
$local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this) toggleFollow: (event) ->
$elem = $(event.target)
handleSubmitEditThread = (elem) -> subscribed = @model.get('subscribed')
url = Discussion.urlFor('update_thread', id) if subscribed
title = $local(".thread-title-edit").val() url = @model.urlFor('unfollow')
body = Discussion.getWmdContent $content, $local, "thread-body-edit" else
tags = $local(".thread-tags-edit").val() url = @model.urlFor('follow')
Discussion.safeAjax DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
$elem: $(elem) @model.set('subscribed', not subscribed)
url: url
type: "POST" toggleClosed: (event) ->
dataType: 'json' $elem = $(event.target)
data: {title: title, body: body, tags: tags}, url = @model.urlFor('close')
error: Discussion.formErrorHandler($local(".discussion-update-errors")) closed = @model.get('closed')
success: (response, textStatus) -> data = { closed: not closed }
Discussion.clearFormErrors($local(".discussion-update-errors")) DiscussionUtil.post $elem, url, data, (response, textStatus) =>
$discussionContent.replaceWith(response.html) @model.set('closed', not closed)
Discussion.extendContentInfo response.content['id'], response['annotated_content_info']
Discussion.initializeContent($content) edit: (event) ->
Discussion.bindContentEvents($content) @$(".discussion-content-wrapper").hide()
$editView = @$(".discussion-content-edit")
handleEditComment = (elem) ->
$local(".discussion-content-wrapper").hide()
$editView = $local(".discussion-content-edit")
if $editView.length if $editView.length
$editView.show() $editView.show()
else else
view = { id: id, body: $local(".comment-raw-body").html() } view = {}
$discussionContent.append Mustache.render Discussion.editCommentTemplate, view view.id = @model.id
Discussion.makeWmdEditor $content, $local, "comment-body-edit" if @model.get('type') == 'thread'
$local(".discussion-submit-update").unbind("click").click -> handleSubmitEditComment(this) view.title = @$(".thread-raw-title").html()
$local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this) view.body = @$(".thread-raw-body").html()
view.tags = @$(".thread-raw-tags").html()
handleSubmitEditComment= (elem) ->
url = Discussion.urlFor('update_comment', id)
body = Discussion.getWmdContent $content, $local, "comment-body-edit"
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: "json"
data: {body: body}
error: Discussion.formErrorHandler($local(".discussion-update-errors"))
success: (response, textStatus) ->
Discussion.clearFormErrors($local(".discussion-update-errors"))
$discussionContent.replaceWith(response.html)
Discussion.extendContentInfo response.content['id'], response['annotated_content_info']
Discussion.initializeContent($content)
Discussion.bindContentEvents($content)
handleEndorse = (elem, endorsed) ->
url = Discussion.urlFor('endorse_comment', id)
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: "json"
data: {endorsed: endorsed}
success: (response, textStatus) ->
if textStatus == "success"
if endorsed
$(content).addClass("endorsed")
else else
$(content).removeClass("endorsed") view.body = @$(".comment-raw-body").html()
@$discussionContent().append Mustache.render DiscussionUtil.getTemplate("_edit_#{@model.get('type')}"), view
$(elem).unbind('click').click -> Discussion.makeWmdEditor @$el, $.proxy(@$, @), "#{@model.get('type')}-body-edit"
handleEndorse(elem, !endorsed) @$(".thread-tags-edit").tagsInput DiscussionUtil.tagsInputOptions()
@$(".discussion-submit-update").unbind("click").click $.proxy(@submitEdit, @)
handleOpenClose = (elem, text) -> @$(".discussion-cancel-update").unbind("click").click $.proxy(@cancelEdit, @)
url = Discussion.urlFor('openclose_thread', id)
closed = undefined submitEdit: (event) ->
if text.match(/Close/)
closed = true url = @model.urlFor('update')
else if text.match(/[Oo]pen/) data = {}
closed = false if @model.get('type') == 'thread'
data.title = @$(".thread-title-edit").val()
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "thread-body-edit"
data.tags = @$(".thread-tags-edit").val()
else else
console.log "Unexpected text " + text + "for open/close thread." data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "comment-body-edit"
DiscussionUtil.safeAjax
Discussion.safeAjax $elem: $(event.target)
$elem: $(elem)
url: url url: url
type: "POST" type: "POST"
dataType: "json" dataType: 'json'
data: {closed: closed} data: data
error: DiscussionUtil.formErrorHandler @$(".discussion-update-errors")
success: (response, textStatus) => success: (response, textStatus) =>
if textStatus == "success" DiscussionUtil.clearFormErrors @$(".discussion-update-errors")
if closed @$discussionContent().replaceWith(response.html)
$(content).addClass("closed") @model.set response.content
$(elem).text "Re-open Thread" @model.updateInfo response.annotated_content_info
else
$(content).removeClass("closed") cancelEdit: (event) ->
$(elem).text "Close Thread" @$(".discussion-content-edit").hide()
error: (response, textStatus, e) -> @$(".discussion-content-wrapper").show()
console.log e
delete: (event) ->
handleDelete = (elem) -> url = @model.urlFor('delete')
if $content.hasClass("thread") if @model.get('type') == 'thread'
url = Discussion.urlFor('delete_thread', id) c = confirm "Are you sure to delete thread \"#{@model.get('title')}\"?"
c = confirm "Are you sure to delete thread \"" + $content.find("a.thread-title").text() + "\"?"
else else
url = Discussion.urlFor('delete_comment', id)
c = confirm "Are you sure to delete this comment? " c = confirm "Are you sure to delete this comment? "
if c != true if not c
return return
Discussion.safeAjax $elem = $(event.target)
$elem: $(elem) DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
url: url @$el.remove()
type: "POST" @model.get('thread').removeComment(@model)
dataType: "json"
data: {} events:
success: (response, textStatus) => "click .discussion-follow-thread": "toggleFollow"
if textStatus == "success" "click .thread-title": "toggleSingleThread"
$(content).remove() "click .discussion-show-comments": "toggleSingleThread"
error: (response, textStatus, e) -> "click .discussion-reply-thread": "reply"
console.log e "click .discussion-reply-comment": "reply"
"click .discussion-cancel-reply": "cancelReply"
handleHideSingleThread = (elem) -> "click .discussion-vote-up": "toggleVote"
$threadTitle = $local(".thread-title") "click .discussion-vote-down": "toggleVote"
$hideComments = $local(".discussion-hide-comments") "click .admin-endorse": "toggleEndorse"
$hideComments.removeClass("discussion-hide-comments") "click .admin-openclose": "toggleClosed"
.addClass("discussion-show-comments") "click .admin-edit": "edit"
$content.children(".comments").hide() "click .admin-delete": "delete"
$threadTitle.unbind('click').click handleShowSingleThread
$hideComments.unbind('click').click handleShowSingleThread initLocal: ->
prevHtml = $hideComments.html() @$local = @$el.children(".local")
$hideComments.html prevHtml.replace "Hide", "Show" @$delegateElement = @$local
handleShowSingleThread = -> initTitle: ->
$threadTitle = $local(".thread-title") $contentTitle = @$(".thread-title")
$showComments = $local(".discussion-show-comments")
if not $showComments.hasClass("first-time") and (not $showComments.length or not $threadTitle.length)
return
rebindHideEvents = ->
$threadTitle.unbind('click').click handleHideSingleThread
$showComments.unbind('click').click handleHideSingleThread
$showComments.removeClass("discussion-show-comments")
.addClass("discussion-hide-comments")
prevHtml = $showComments.html()
$showComments.html prevHtml.replace "Show", "Hide"
if not $showComments.hasClass("first-time") and $content.children(".comments").length
$content.children(".comments").show()
rebindHideEvents()
else
discussion_id = $threadTitle.parents(".discussion").attr("_id")
url = Discussion.urlFor('retrieve_single_thread', discussion_id, id)
Discussion.safeAjax
$elem: $.merge($threadTitle, $showComments)
url: url
type: "GET"
dataType: 'json'
success: (response, textStatus) ->
Discussion.bulkExtendContentInfo response['annotated_content_info']
$content.append(response['html'])
$content.find(".comment").each (index, comment) ->
Discussion.initializeContent(comment)
Discussion.bindContentEvents(comment)
$showComments.removeClass("first-time")
rebindHideEvents()
Discussion.bindLocalEvents $local,
"click .thread-title": ->
handleShowSingleThread(this)
"click .discussion-show-comments": ->
handleShowSingleThread(this)
"click .discussion-hide-comments": ->
handleHideSingleThread(this)
"click .discussion-reply-thread": ->
handleShowSingleThread($local(".thread-title"))
handleReply(this)
"click .discussion-reply-comment": ->
handleReply(this)
"click .discussion-cancel-reply": ->
handleCancelReply(this)
"click .discussion-vote-up": ->
$elem = $(this)
if $elem.hasClass("voted")
handleUnvote($elem)
else
handleVote($elem, "up")
"click .discussion-vote-down": ->
$elem = $(this)
if $elem.hasClass("voted")
handleUnvote($elem)
else
handleVote($elem, "down")
"click .admin-endorse": ->
handleEndorse(this, not $content.hasClass("endorsed"))
"click .admin-openclose": ->
handleOpenClose(this, $(this).text())
"click .admin-edit": ->
if $content.hasClass("thread")
handleEditThread(this)
else
handleEditComment(this)
"click .admin-delete": ->
handleDelete(this)
initializeContent: (content) ->
unescapeHighlightTag = (text) ->
text.replace(/\&lt\;highlight\&gt\;/g, "<span class='search-highlight'>")
.replace(/\&lt\;\/highlight\&gt\;/g, "</span>")
stripHighlight = (text, type) ->
text.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "")
.replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "")
stripLatexHighlight = (text) ->
Discussion.processEachMathAndCode text, stripHighlight
markdownWithHighlight = (text) ->
converter = Markdown.getMathCompatibleConverter()
unescapeHighlightTag stripLatexHighlight converter.makeHtml text
$content = $(content)
initializeVote $content
if $content.hasClass("thread")
initializeFollowThread $content
$local = Discussion.generateLocal($content.children(".discussion-content"))
$local("span.timeago").timeago()
$contentTitle = $local(".thread-title")
if $contentTitle.length if $contentTitle.length
$contentTitle.html unescapeHighlightTag stripLatexHighlight $contentTitle.html() $contentTitle.html DiscussionUtil.unescapeHighlightTag DiscussionUtil.stripLatexHighlight $contentTitle.html()
$contentBody = $local(".content-body")
$contentBody.html Discussion.postMathJaxProcessor markdownWithHighlight $contentBody.html()
initBody: ->
$contentBody = @$(".content-body")
$contentBody.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight $contentBody.html()
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")] MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
id = $content.attr("_id")
if $content.hasClass("thread") initTimeago: ->
discussion_id = $content.attr("_discussion_id") @$("span.timeago").timeago()
permalink = Discussion.urlFor("permanent_link_thread", discussion_id, id)
else initPermalink: ->
thread_id = $content.parents(".thread").attr("_id") @$(".discussion-permanent-link").attr "href", @model.permalink()
discussion_id = $content.parents(".thread").attr("_discussion_id")
permalink = Discussion.urlFor("permanent_link_comment", discussion_id, thread_id, id) renderPartial: ->
$local(".discussion-permanent-link").attr "href", permalink for attr, value of @model.changedAttributes()
if @partial[attr]
if not Discussion.getContentInfo id, 'editable' @partial[attr].apply(@, [value])
$local(".admin-edit").remove()
if not Discussion.getContentInfo id, 'can_reply' initBindings: ->
$local(".discussion-reply").remove() @model.view = @
if not Discussion.getContentInfo id, 'can_endorse' @model.bind('change', @renderPartial, @)
$local(".admin-endorse").remove()
if not Discussion.getContentInfo id, 'can_delete' initialize: ->
$local(".admin-delete").remove() @initBindings()
if not Discussion.getContentInfo id, 'can_openclose' @initLocal()
$local(".admin-openclose").remove() @initTimeago()
#if not Discussion.getContentInfo id, 'can_vote' @initTitle()
# $local(".discussion-vote").css "visibility", "hidden" @initBody()
@initCommentViews()
class @Thread extends @Content
urlMappers:
'retrieve' : -> DiscussionUtil.urlFor('retrieve_single_thread', @discussion.id, @id)
'reply' : -> DiscussionUtil.urlFor('create_comment', @id)
'unvote' : -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
'upvote' : -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
'downvote' : -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
'close' : -> DiscussionUtil.urlFor('openclose_thread', @id)
'update' : -> DiscussionUtil.urlFor('update_thread', @id)
'delete' : -> DiscussionUtil.urlFor('delete_thread', @id)
'follow' : -> DiscussionUtil.urlFor('follow_thread', @id)
'unfollow' : -> DiscussionUtil.urlFor('unfollow_thread', @id)
initialize: ->
@set('thread', @)
super()
permalink: ->
discussion_id = @get('commentable_id')
return Discussion.urlFor("permanent_link_thread", discussion_id, @id)
class @ThreadView extends @ContentView
class @Comment extends @Content
urlMappers:
'reply': -> DiscussionUtil.urlFor('create_sub_comment', @id)
'unvote': -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
'upvote': -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
'downvote': -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
'endorse': -> DiscussionUtil.urlFor('endorse_comment', @id)
'update': -> DiscussionUtil.urlFor('update_comment', @id)
'delete': -> DiscussionUtil.urlFor('delete_comment', @id)
permalink: ->
thread_id = @get('thread').id
discussion_id = @get('thread').get('commentable_id')
return Discussion.urlFor("permanent_link_comment", discussion_id, thread_id, @id)
getCommentsCount: ->
count = 0
@get('comments').each (comment) ->
count += comment.getCommentsCount() + 1
count
class @CommentView extends @ContentView
class @Comments extends Backbone.Collection
model: Comment
initialize: ->
@bind "add", (item) =>
item.collection = @
find: (id) ->
_.first @where(id: id)
if not @Discussion? class @Discussion extends Backbone.Collection
@Discussion = {} model: Thread
Discussion = @Discussion initialize: ->
DiscussionUtil.addDiscussion @id, @
initializeFollowDiscussion = (discussion) -> @bind "add", (item) =>
$discussion = $(discussion) item.discussion = @
id = $following.attr("_id")
$local = Discussion.generateLocal() find: (id) ->
$discussion.children(".discussion-non-content") _.first @where(id: id)
.find(".discussion-title-wrapper")
.append(Discussion.subscriptionLink('discussion', id)) addThread: (thread, options) ->
options ||= {}
@Discussion = $.extend @Discussion, model = new Thread thread
@add model
initializeDiscussion: (discussion) -> model
$discussion = $(discussion)
$discussion.find(".thread").each (index, thread) -> class @DiscussionView extends Backbone.View
Discussion.initializeContent(thread)
Discussion.bindContentEvents(thread) $: (selector) ->
$discussion.find(".comment").each (index, comment) -> @$local.find(selector)
Discussion.initializeContent(comment)
Discussion.bindContentEvents(comment) initLocal: ->
@$local = @$el.children(".local")
#initializeFollowDiscussion(discussion) TODO move this somewhere else @$delegateElement = @$local
bindDiscussionEvents: (discussion) -> initialize: ->
@initLocal()
$discussion = $(discussion) @model.id = @$el.attr("_id")
$discussionNonContent = $discussion.children(".discussion-non-content") @model.view = @
$local = Discussion.generateLocal($discussion.children(".discussion-local")) @$el.children(".threads").children(".thread").each (index, elem) =>
threadView = new ThreadView el: elem, model: @model.find $(elem).attr("_id")
id = $discussion.attr("_id") if @$el.hasClass("forum-discussion")
$(".discussion-sidebar").find(".sidebar-new-post-button")
handleSubmitNewPost = (elem) -> .unbind('click').click $.proxy @newPost, @
title = $local(".new-post-title").val() else if @$el.hasClass("inline-discussion")
body = Discussion.getWmdContent $discussion, $local, "new-post-body" @newPost()
tags = $local(".new-post-tags").val()
url = Discussion.urlFor('create_thread', id) reload: ($elem, url) ->
Discussion.safeAjax if not url then return
$elem: $(elem) DiscussionUtil.get $elem, url, {}, (response, textStatus) =>
url: url $parent = @$el.parent()
type: "POST" @$el.replaceWith(response.html)
dataType: 'json' $discussion = $parent.find("section.discussion")
data: @model.reset(response.discussionData, { silent: false })
title: title view = new DiscussionView el: $discussion[0], model: @model
body: body DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
tags: tags
error: Discussion.formErrorHandler($local(".new-post-form-errors")) loadSimilarPost: (event) ->
success: (response, textStatus) -> $title = @$(".new-post-title")
Discussion.clearFormErrors($local(".new-post-form-errors")) $wrapper = @$(".new-post-similar-posts-wrapper")
$thread = $(response.html) $similarPosts = @$(".new-post-similar-posts")
$discussion.children(".threads").prepend($thread)
$local(".new-post-title").val("")
Discussion.setWmdContent $discussion, $local, "new-post-body", ""
$local(".new-post-tags").val("")
if $discussion.hasClass("inline-discussion")
$local(".new-post-form").addClass("collapsed")
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").hide()
handleCancelNewPost = (elem) ->
if $discussion.hasClass("inline-discussion")
$local(".new-post-form").addClass("collapsed")
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").hide()
handleSimilarPost = (elem) ->
$title = $local(".new-post-title")
$wrapper = $local(".new-post-similar-posts-wrapper")
$similarPosts = $local(".new-post-similar-posts")
prevText = $title.attr("prev-text") prevText = $title.attr("prev-text")
text = $title.val() text = $title.val()
if text == prevText if text == prevText
if $local(".similar-post").length if @$(".similar-post").length
$wrapper.show() $wrapper.show()
else if $.trim(text).length else if $.trim(text).length
Discussion.safeAjax $elem = $(event.target)
$elem: $(elem) url = DiscussionUtil.urlFor 'search_similar_threads', @model.id
url: Discussion.urlFor 'search_similar_threads', id data = { text: @$(".new-post-title").val() }
type: "GET" DiscussionUtil.get $elem, url, data, (response, textStatus) =>
dateType: 'json'
data:
text: $local(".new-post-title").val()
success: (response, textStatus) ->
$similarPosts.empty() $similarPosts.empty()
console.log response
if $.type(response) == "array" and response.length if $.type(response) == "array" and response.length
$wrapper.show() $wrapper.show()
for thread in response for thread in response
#singleThreadUrl = Discussion.urlFor 'retrieve_single_thread
$similarPost = $("<a>").addClass("similar-post") $similarPost = $("<a>").addClass("similar-post")
.html(thread["title"]) .html(thread["title"])
.attr("href", "javascript:void(0)") #TODO .attr("href", "javascript:void(0)") #TODO
...@@ -99,92 +74,94 @@ initializeFollowDiscussion = (discussion) -> ...@@ -99,92 +74,94 @@ initializeFollowDiscussion = (discussion) ->
$wrapper.hide() $wrapper.hide()
$title.attr("prev-text", text) $title.attr("prev-text", text)
initializeNewPost = ->
view = { discussion_id: id }
$discussionNonContent = $discussion.children(".discussion-non-content")
if not $local(".wmd-panel").length newPost: ->
$discussionNonContent.append Mustache.render Discussion.newPostTemplate, view if not @$(".wmd-panel").length
$newPostBody = $local(".new-post-body") view = { discussion_id: @model.id }
Discussion.makeWmdEditor $discussion, $local, "new-post-body" @$el.children(".discussion-non-content").append Mustache.render DiscussionUtil.getTemplate("_new_post"), view
$newPostBody = @$(".new-post-body")
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body"
$input = Discussion.getWmdInput($discussion, $local, "new-post-body") $input = DiscussionUtil.getWmdInput @$el, $.proxy(@$, @), "new-post-body"
$input.attr("placeholder", "post a new topic...") $input.attr("placeholder", "post a new topic...")
if $discussion.hasClass("inline-discussion") if @$el.hasClass("inline-discussion")
$input.bind 'focus', (e) -> $input.bind 'focus', (e) =>
$local(".new-post-form").removeClass('collapsed') @$(".new-post-form").removeClass('collapsed')
else if $discussion.hasClass("forum-discussion") else if @$el.hasClass("forum-discussion")
$local(".new-post-form").removeClass('collapsed') @$(".new-post-form").removeClass('collapsed')
$local(".new-post-tags").tagsInput Discussion.tagsInputOptions() @$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions()
$local(".new-post-title").blur -> @$(".new-post-title").blur $.proxy(@loadSimilarPost, @)
handleSimilarPost(this)
$local(".hide-similar-posts").click -> @$(".hide-similar-posts").click =>
$local(".new-post-similar-posts-wrapper").hide() @$(".new-post-similar-posts-wrapper").hide()
$local(".discussion-submit-post").click -> @$(".discussion-submit-post").click $.proxy(@submitNewPost, @)
handleSubmitNewPost(this) @$(".discussion-cancel-post").click $.proxy(@cancelNewPost, @)
$local(".discussion-cancel-post").click ->
handleCancelNewPost(this)
$local(".new-post-form").show()
handleAjaxReloadDiscussion = (elem, url) -> @$(".new-post-form").show()
if not url then return
$elem = $(elem) submitNewPost: (event) ->
$discussion = $elem.parents("section.discussion") title = @$(".new-post-title").val()
Discussion.safeAjax body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "new-post-body"
$elem: $elem tags = @$(".new-post-tags").val()
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
autowatch = false || @$(".discussion-auto-watch").is(":checked")
url = DiscussionUtil.urlFor('create_thread', @model.id)
DiscussionUtil.safeAjax
$elem: $(event.target)
url: url url: url
type: "GET" type: "POST"
dataType: 'html' dataType: 'json'
success: (data, textStatus) -> data:
$data = $(data) title: title
$parent = $discussion.parent() body: body
$discussion.replaceWith($data) tags: tags
$discussion = $parent.children(".discussion") anonymous: anonymous
Discussion.initializeDiscussion($discussion) auto_subscribe: autowatch
Discussion.bindDiscussionEvents($discussion) error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors"))
success: (response, textStatus) =>
handleAjaxSearch = (elem) -> DiscussionUtil.clearFormErrors(@$(".new-post-form-errors"))
$elem = $(elem) $thread = $(response.html)
url = URI($elem.attr("action")).addSearch({text: $local(".search-input").val()}) @$el.children(".threads").prepend($thread)
handleAjaxReloadDiscussion($elem, url)
handleAjaxSort = (elem) ->
$elem = $(elem)
url = $elem.attr("sort-url")
handleAjaxReloadDiscussion($elem, url)
handleAjaxPage = (elem) -> @$(".new-post-title").val("")
$elem = $(elem) DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "new-post-body", ""
url = $elem.attr("page-url") @$(".new-post-tags").val("")
handleAjaxReloadDiscussion($elem, url) @$(".new-post-tags").importTags("")
if $discussion.hasClass("inline-discussion") thread = @model.addThread response.content
initializeNewPost() threadView = new ThreadView el: $thread[0], model: thread
thread.updateInfo response.annotated_content_info
@cancelNewPost()
if $discussion.hasClass("forum-discussion")
$discussionSidebar = $(".discussion-sidebar")
if $discussionSidebar.length
$sidebarLocal = Discussion.generateLocal($discussionSidebar)
Discussion.bindLocalEvents $sidebarLocal,
"click .sidebar-new-post-button": (event) ->
initializeNewPost()
Discussion.bindLocalEvents $local, cancelNewPost: (event) ->
if @$el.hasClass("inline-discussion")
@$(".new-post-form").addClass("collapsed")
else if @$el.hasClass("forum-discussion")
@$(".new-post-form").hide()
"submit .search-wrapper>.discussion-search-form": (event) -> search: (event) ->
event.preventDefault() event.preventDefault()
handleAjaxSearch(this) $elem = $(event.target)
url = URI($elem.attr("action")).addSearch({text: @$(".search-input").val()})
@reload($elem, url)
"click .discussion-search-link": -> sort: ->
handleAjaxSearch($local(".search-wrapper>.discussion-search-form")) $elem = $(event.target)
url = $elem.attr("sort-url")
@reload($elem, url)
"click .discussion-sort-link": -> page: (event) ->
handleAjaxSort(this) $elem = $(event.target)
url = $elem.attr("page-url")
@reload($elem, url)
$discussion.children(".discussion-paginator").find(".discussion-page-link").unbind('click').click -> events:
handleAjaxPage(this) "submit .search-wrapper>.discussion-search-form": "search"
"click .discussion-search-link": "search"
"click .discussion-sort-link": "sort"
"click .discussion-page-link": "page"
if not @Discussion? class @DiscussionModuleView extends Backbone.View
@Discussion = {} events:
"click .discussion-show": "toggleDiscussion"
Discussion = @Discussion toggleDiscussion: (event) ->
if @showed
@Discussion = $.extend @Discussion, @$("section.discussion").hide()
initializeDiscussionModule: (elem) -> $(event.target).html("Show Discussion")
$discussionModule = $(elem) @showed = false
$local = Discussion.generateLocal($discussionModule) else
handleShowDiscussion = (elem) -> if @retrieved
$elem = $(elem) @$("section.discussion").show()
if not $local("section.discussion").length $(event.target).html("Hide Discussion")
@showed = true
else
$elem = $(event.target)
discussion_id = $elem.attr("discussion_id") discussion_id = $elem.attr("discussion_id")
url = Discussion.urlFor 'retrieve_discussion', discussion_id url = DiscussionUtil.urlFor 'retrieve_discussion', discussion_id
Discussion.safeAjax Discussion.safeAjax
$elem: $elem $elem: $elem
url: url url: url
type: "GET" type: "GET"
success: (data, textStatus, xhr) -> dataType: 'json'
$discussionModule.append(data) success: (response, textStatus) =>
discussion = $local("section.discussion") @$el.append(response.html)
Discussion.initializeDiscussion(discussion) $discussion = @$el.find("section.discussion")
Discussion.bindDiscussionEvents(discussion) $(event.target).html("Hide Discussion")
$elem.html("Hide Discussion") discussion = new Discussion()
$elem.unbind('click').click -> discussion.reset(response.discussionData, {silent: false})
handleHideDiscussion(this) view = new DiscussionView(el: $discussion[0], model: discussion)
dataType: 'html' DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
else @retrieved = true
$local("section.discussion").show() @showed = true
$elem.html("Hide Discussion")
$elem.unbind('click').click ->
handleHideDiscussion(this)
handleHideDiscussion = (elem) ->
$local("section.discussion").hide()
$elem = $(elem)
$elem.html("Show Discussion")
$elem.unbind('click').click ->
handleShowDiscussion(this)
$local(".discussion-show").click ->
handleShowDiscussion(this)
$ -> $ ->
#toggle = -> window.$$contents = {}
# $('.course-wrapper').toggleClass('closed') window.$$discussions = {}
#Discussion = window.Discussion $(".discussion-module").each (index, elem) ->
#if $('#accordion').length view = new DiscussionModuleView(el: elem)
# active = $('#accordion ul:has(li.active)').index('#accordion ul')
# $('#accordion').bind('accordionchange', @log).accordion
# active: if active >= 0 then active else 1
# header: 'h3'
# autoHeight: false
# $('#open_close_accordion a').click toggle
# $('#accordion').show()
#$(".discussion-module").each (index, elem) -> $("section.discussion").each (index, elem) ->
# Discussion.initializeDiscussionModule(elem) discussionData = DiscussionUtil.getDiscussionData($(elem).attr("_id"))
discussion = new Discussion()
discussion.reset(discussionData, {silent: false})
view = new DiscussionView(el: elem, model: discussion)
#$("section.discussion").each (index, discussion) -> DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
# Discussion.initializeDiscussion(discussion)
# Discussion.bindDiscussionEvents(discussion)
#Discussion.initializeUserProfile($(".discussion-sidebar>.user-profile"))
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
@Discussion = $.extend @Discussion,
newPostTemplate: """
<form class="new-post-form collapsed" id="new-post-form" style="display: block; ">
<ul class="new-post-form-errors discussion-errors"></ul>
<input type="text" class="new-post-title title-input" placeholder="Title" />
<div class="new-post-similar-posts-wrapper" style="display: none">
Similar Posts:
<a class="hide-similar-posts" href="javascript:void(0)">Hide</a>
<div class="new-post-similar-posts"></div>
</div>
<div class="new-post-body reply-body"></div>
<input class="new-post-tags" placeholder="Tags" />
<div class="post-options">
<input type="checkbox" class="discussion-post-anonymously" id="discussion-post-anonymously-${discussion_id}">
<label for="discussion-post-anonymously-${discussion_id}">post anonymously</label>
<input type="checkbox" class="discussion-auto-watch" id="discussion-autowatch-${discussion_id}" checked="">
<label for="discussion-auto-watch-${discussion_id}">follow this thread</label>
</div>
<div class="new-post-control post-control">
<a class="discussion-cancel-post" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-post control-button" href="javascript:void(0)">Submit</a>
</div>
</form>
"""
replyTemplate: """
<form class="discussion-reply-new">
<ul class="discussion-errors"></ul>
<div class="reply-body"></div>
<input type="checkbox" class="discussion-post-anonymously" id="discussion-post-anonymously-{{id}}" />
<label for="discussion-post-anonymously-{{id}}">post anonymously</label>
{{#showWatchCheckbox}}
<input type="checkbox" class="discussion-auto-watch" id="discussion-autowatch-{{id}}" checked />
<label for="discussion-auto-watch-{{id}}">follow this thread</label>
{{/showWatchCheckbox}}
<br />
<div class = "reply-post-control">
<a class="discussion-cancel-post" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-post control-button" href="javascript:void(0)">Submit</a>
</div>
</form>
"""
editThreadTemplate: """
<form class="discussion-content-edit discussion-thread-edit" _id="{{id}}">
<ul class="discussion-errors discussion-update-errors"></ul>
<input type="text" class="thread-title-edit title-input" placeholder="Title" value="{{title}}"/>
<div class="thread-body-edit body-input">{{body}}</div>
<input class="thread-tags-edit" placeholder="Tags" value="{{tags}}" />
<div class = "edit-post-control">
<a class="discussion-cancel-update" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-update control-button" href="javascript:void(0)">Update</a>
</div>
</form>
"""
editCommentTemplate: """
<form class="discussion-content-edit discussion-comment-edit" _id="{{id}}">
<ul class="discussion-errors discussion-update-errors"></ul>
<div class="comment-body-edit body-input">{{body}}</div>
<div class = "edit-post-control">
<a class="discussion-cancel-update" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-update control-button" href="javascript:void(0)">Update</a>
</div>
</form>
"""
if not @Discussion? class @DiscussionUtil
@Discussion = {}
Discussion = @Discussion @wmdEditors: {}
wmdEditors = {} @getTemplate: (id) ->
$("script##{id}").html()
@Discussion = $.extend @Discussion, @getDiscussionData: (id) ->
return $$discussion_data[id]
generateLocal: (elem) -> @addContent: (id, content) -> window.$$contents[id] = content
(selector) -> $(elem).find(selector)
generateDiscussionLink: (cls, txt, handler) -> @getContent: (id) -> window.$$contents[id]
@addDiscussion: (id, discussion) -> window.$$discussions[id] = discussion
@getDiscussion: (id) -> window.$$discussions[id]
@bulkUpdateContentInfo: (infos) ->
for id, info of infos
@getContent(id).updateInfo(info)
@generateDiscussionLink: (cls, txt, handler) ->
$("<a>").addClass("discussion-link") $("<a>").addClass("discussion-link")
.attr("href", "javascript:void(0)") .attr("href", "javascript:void(0)")
.addClass(cls).html(txt) .addClass(cls).html(txt)
.click -> handler(this) .click -> handler(this)
urlFor: (name, param, param1, param2) -> @urlFor: (name, param, param1, param2) ->
{ {
follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow" follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow"
unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow" unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow"
...@@ -48,7 +58,7 @@ wmdEditors = {} ...@@ -48,7 +58,7 @@ wmdEditors = {}
permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}" permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}"
}[name] }[name]
safeAjax: (params) -> @safeAjax: (params) ->
$elem = params.$elem $elem = params.$elem
if $elem.attr("disabled") if $elem.attr("disabled")
return return
...@@ -56,17 +66,31 @@ wmdEditors = {} ...@@ -56,17 +66,31 @@ wmdEditors = {}
$.ajax(params).always -> $.ajax(params).always ->
$elem.removeAttr("disabled") $elem.removeAttr("disabled")
handleAnchorAndReload: (response) -> @get: ($elem, url, data, success) ->
#window.location = window.location.pathname + "#" + response['id'] @safeAjax
window.location.reload() $elem: $elem
url: url
type: "GET"
dataType: "json"
data: data
success: success
@post: ($elem, url, data, success) ->
@safeAjax
$elem: $elem
url: url
type: "POST"
dataType: "json"
data: data
success: success
bindLocalEvents: ($local, eventsHandler) -> @bindLocalEvents: ($local, eventsHandler) ->
for eventSelector, handler of eventsHandler for eventSelector, handler of eventsHandler
[event, selector] = eventSelector.split(' ') [event, selector] = eventSelector.split(' ')
$local(selector).unbind(event)[event] handler $local(selector).unbind(event)[event] handler
tagsInputOptions: -> @tagsInputOptions: ->
autocomplete_url: Discussion.urlFor('tags_autocomplete') autocomplete_url: @urlFor('tags_autocomplete')
autocomplete: autocomplete:
remoteDataType: 'json' remoteDataType: 'json'
interactive: true interactive: true
...@@ -75,23 +99,7 @@ wmdEditors = {} ...@@ -75,23 +99,7 @@ wmdEditors = {}
defaultText: "Tag your post: press enter after each tag" defaultText: "Tag your post: press enter after each tag"
removeWithBackspace: true removeWithBackspace: true
isSubscribed: (id, type) -> @formErrorHandler: (errorsField) ->
$$user_info? and (
if type == "thread"
id in $$user_info.subscribed_thread_ids
else if type == "commentable" or type == "discussion"
id in $$user_info.subscribed_commentable_ids
else
id in $$user_info.subscribed_user_ids
)
isUpvoted: (id) ->
$$user_info? and (id in $$user_info.upvoted_ids)
isDownvoted: (id) ->
$$user_info? and (id in $$user_info.downvoted_ids)
formErrorHandler: (errorsField) ->
(xhr, textStatus, error) -> (xhr, textStatus, error) ->
response = JSON.parse(xhr.responseText) response = JSON.parse(xhr.responseText)
if response.errors? and response.errors.length > 0 if response.errors? and response.errors.length > 0
...@@ -99,13 +107,13 @@ wmdEditors = {} ...@@ -99,13 +107,13 @@ wmdEditors = {}
for error in response.errors for error in response.errors
errorsField.append($("<li>").addClass("new-post-form-error").html(error)) errorsField.append($("<li>").addClass("new-post-form-error").html(error))
clearFormErrors: (errorsField) -> @clearFormErrors: (errorsField) ->
errorsField.empty() errorsField.empty()
postMathJaxProcessor: (text) -> @postMathJaxProcessor: (text) ->
RE_INLINEMATH = /^\$([^\$]*)\$/g RE_INLINEMATH = /^\$([^\$]*)\$/g
RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g
Discussion.processEachMathAndCode text, (s, type) -> @processEachMathAndCode text, (s, type) ->
if type == 'display' if type == 'display'
s.replace RE_DISPLAYMATH, ($0, $1) -> s.replace RE_DISPLAYMATH, ($0, $1) ->
"\\[" + $1 + "\\]" "\\[" + $1 + "\\]"
...@@ -115,61 +123,43 @@ wmdEditors = {} ...@@ -115,61 +123,43 @@ wmdEditors = {}
else else
s s
makeWmdEditor: ($content, $local, cls_identifier) -> @makeWmdEditor: ($content, $local, cls_identifier) ->
elem = $local(".#{cls_identifier}") elem = $local(".#{cls_identifier}")
id = $content.attr("_id") id = $content.attr("_id")
appended_id = "-#{cls_identifier}-#{id}" appended_id = "-#{cls_identifier}-#{id}"
imageUploadUrl = Discussion.urlFor('upload') imageUploadUrl = @urlFor('upload')
editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, Discussion.postMathJaxProcessor _processor = (_this) ->
wmdEditors["#{cls_identifier}-#{id}"] = editor (text) -> _this.postMathJaxProcessor(text)
editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, _processor(@)
@wmdEditors["#{cls_identifier}-#{id}"] = editor
editor editor
getWmdEditor: ($content, $local, cls_identifier) -> @getWmdEditor: ($content, $local, cls_identifier) ->
id = $content.attr("_id") id = $content.attr("_id")
wmdEditors["#{cls_identifier}-#{id}"] @wmdEditors["#{cls_identifier}-#{id}"]
getWmdInput: ($content, $local, cls_identifier) -> @getWmdInput: ($content, $local, cls_identifier) ->
id = $content.attr("_id") id = $content.attr("_id")
$local("#wmd-input-#{cls_identifier}-#{id}") $local("#wmd-input-#{cls_identifier}-#{id}")
getWmdContent: ($content, $local, cls_identifier) -> @getWmdContent: ($content, $local, cls_identifier) ->
Discussion.getWmdInput($content, $local, cls_identifier).val() @getWmdInput($content, $local, cls_identifier).val()
setWmdContent: ($content, $local, cls_identifier, text) -> @setWmdContent: ($content, $local, cls_identifier, text) ->
Discussion.getWmdInput($content, $local, cls_identifier).val(text) @getWmdInput($content, $local, cls_identifier).val(text)
Discussion.getWmdEditor($content, $local, cls_identifier).refreshPreview() @getWmdEditor($content, $local, cls_identifier).refreshPreview()
getContentInfo: (id, attr) -> @subscriptionLink: (type, id) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
(window.$$annotated_content_info[id] || {})[attr]
setContentInfo: (id, attr, value) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info[id] ||= {}
window.$$annotated_content_info[id][attr] = value
extendContentInfo: (id, newInfo) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info[id] = newInfo
bulkExtendContentInfo: (newInfos) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info = $.extend window.$$annotated_content_info, newInfos
subscriptionLink: (type, id) ->
followLink = -> followLink = ->
Discussion.generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow) @generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow)
unfollowLink = -> unfollowLink = ->
Discussion.generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow) @generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow)
handleFollow = (elem) -> handleFollow = (elem) ->
Discussion.safeAjax @safeAjax
$elem: $(elem) $elem: $(elem)
url: Discussion.urlFor("follow_#{type}", id) url: @urlFor("follow_#{type}", id)
type: "POST" type: "POST"
success: (response, textStatus) -> success: (response, textStatus) ->
if textStatus == "success" if textStatus == "success"
...@@ -177,21 +167,21 @@ wmdEditors = {} ...@@ -177,21 +167,21 @@ wmdEditors = {}
dataType: 'json' dataType: 'json'
handleUnfollow = (elem) -> handleUnfollow = (elem) ->
Discussion.safeAjax @safeAjax
$elem: $(elem) $elem: $(elem)
url: Discussion.urlFor("unfollow_#{type}", id) url: @urlFor("unfollow_#{type}", id)
type: "POST" type: "POST"
success: (response, textStatus) -> success: (response, textStatus) ->
if textStatus == "success" if textStatus == "success"
$(elem).replaceWith followLink() $(elem).replaceWith followLink()
dataType: 'json' dataType: 'json'
if Discussion.isSubscribed(id, type) if @isSubscribed(id, type)
unfollowLink() unfollowLink()
else else
followLink() followLink()
processEachMathAndCode: (text, processor) -> @processEachMathAndCode: (text, processor) ->
codeArchive = [] codeArchive = []
...@@ -242,3 +232,18 @@ wmdEditors = {} ...@@ -242,3 +232,18 @@ wmdEditors = {}
text = $div.html() text = $div.html()
text text
@unescapeHighlightTag: (text) ->
text.replace(/\&lt\;highlight\&gt\;/g, "<span class='search-highlight'>")
.replace(/\&lt\;\/highlight\&gt\;/g, "</span>")
@stripHighlight: (text) ->
text.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "")
.replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "")
@stripLatexHighlight: (text) ->
@processEachMathAndCode text, @stripHighlight
@markdownWithHighlight: (text) ->
converter = Markdown.getMathCompatibleConverter()
@unescapeHighlightTag @stripLatexHighlight converter.makeHtml text
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
initializeVote = (content) ->
$content = $(content)
$local = Discussion.generateLocal($content.children(".discussion-content"))
id = $content.attr("_id")
if Discussion.isUpvoted id
$local(".discussion-vote-up").addClass("voted")
else if Discussion.isDownvoted id
$local(".discussion-vote-down").addClass("voted")
initializeFollowThread = (thread) ->
$thread = $(thread)
id = $thread.attr("_id")
$thread.children(".discussion-content")
.find(".follow-wrapper")
.append(Discussion.subscriptionLink('thread', id))
@Discussion = $.extend @Discussion,
bindContentEvents: (content) ->
$content = $(content)
$discussionContent = $content.children(".discussion-content")
$local = Discussion.generateLocal($discussionContent)
id = $content.attr("_id")
handleReply = (elem) ->
$replyView = $local(".discussion-reply-new")
if $replyView.length
$replyView.show()
else
thread_id = $discussionContent.parents(".thread").attr("_id")
view =
id: id
showWatchCheckbox: not Discussion.isSubscribed(thread_id, "thread")
$discussionContent.append Mustache.render Discussion.replyTemplate, view
Discussion.makeWmdEditor $content, $local, "reply-body"
$local(".discussion-submit-post").click -> handleSubmitReply(this)
$local(".discussion-cancel-post").click -> handleCancelReply(this)
$local(".discussion-reply").hide()
$local(".discussion-edit").hide()
handleCancelReply = (elem) ->
$replyView = $local(".discussion-reply-new")
if $replyView.length
$replyView.hide()
$local(".discussion-reply").show()
$local(".discussion-edit").show()
handleSubmitReply = (elem) ->
if $content.hasClass("thread")
url = Discussion.urlFor('create_comment', id)
else if $content.hasClass("comment")
url = Discussion.urlFor('create_sub_comment', id)
else
return
body = Discussion.getWmdContent $content, $local, "reply-body"
anonymous = false || $local(".discussion-post-anonymously").is(":checked")
autowatch = false || $local(".discussion-auto-watch").is(":checked")
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: 'json'
data:
body: body
anonymous: anonymous
autowatch: autowatch
error: Discussion.formErrorHandler($local(".discussion-errors"))
success: (response, textStatus) ->
Discussion.clearFormErrors($local(".discussion-errors"))
$comment = $(response.html)
$content.children(".comments").prepend($comment)
Discussion.setWmdContent $content, $local, "reply-body", ""
Discussion.setContentInfo response.content['id'], 'can_reply', true
Discussion.setContentInfo response.content['id'], 'editable', true
Discussion.extendContentInfo response.content['id'], response['annotated_content_info']
Discussion.initializeContent($comment)
Discussion.bindContentEvents($comment)
@cancelReply()
$local(".discussion-reply-new").hide()
$local(".discussion-reply").show()
$local(".discussion-edit").show()
$discussionContent.attr("status", "normal")
handleVote = (elem, value) ->
contentType = if $content.hasClass("thread") then "thread" else "comment"
url = Discussion.urlFor("#{value}vote_#{contentType}", id)
Discussion.safeAjax
$elem: $local(".discussion-vote")
url: url
type: "POST"
dataType: "json"
success: (response, textStatus) ->
if textStatus == "success"
$local(".discussion-vote").removeClass("voted")
$local(".discussion-vote-#{value}").addClass("voted")
$local(".discussion-votes-point").html response.votes.point
handleUnvote = (elem, value) ->
contentType = if $content.hasClass("thread") then "thread" else "comment"
url = Discussion.urlFor("undo_vote_for_#{contentType}", id)
Discussion.safeAjax
$elem: $local(".discussion-vote")
url: url
type: "POST"
dataType: "json"
success: (response, textStatus) ->
if textStatus == "success"
$local(".discussion-vote").removeClass("voted")
$local(".discussion-votes-point").html response.votes.point
handleCancelEdit = (elem) ->
$local(".discussion-content-edit").hide()
$local(".discussion-content-wrapper").show()
handleEditThread = (elem) ->
$local(".discussion-content-wrapper").hide()
$editView = $local(".discussion-content-edit")
if $editView.length
$editView.show()
else
view = {
id: id
title: $local(".thread-raw-title").html()
body: $local(".thread-raw-body").html()
tags: $local(".thread-raw-tags").html()
}
$discussionContent.append Mustache.render Discussion.editThreadTemplate, view
Discussion.makeWmdEditor $content, $local, "thread-body-edit"
$local(".thread-tags-edit").tagsInput Discussion.tagsInputOptions()
$local(".discussion-submit-update").unbind("click").click -> handleSubmitEditThread(this)
$local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this)
handleSubmitEditThread = (elem) ->
url = Discussion.urlFor('update_thread', id)
title = $local(".thread-title-edit").val()
body = Discussion.getWmdContent $content, $local, "thread-body-edit"
tags = $local(".thread-tags-edit").val()
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: 'json'
data: {title: title, body: body, tags: tags},
error: Discussion.formErrorHandler($local(".discussion-update-errors"))
success: (response, textStatus) ->
Discussion.clearFormErrors($local(".discussion-update-errors"))
$discussionContent.replaceWith(response.html)
Discussion.extendContentInfo response.content['id'], response['annotated_content_info']
Discussion.initializeContent($content)
Discussion.bindContentEvents($content)
handleEditComment = (elem) ->
$local(".discussion-content-wrapper").hide()
$editView = $local(".discussion-content-edit")
if $editView.length
$editView.show()
else
view = { id: id, body: $local(".comment-raw-body").html() }
$discussionContent.append Mustache.render Discussion.editCommentTemplate, view
Discussion.makeWmdEditor $content, $local, "comment-body-edit"
$local(".discussion-submit-update").unbind("click").click -> handleSubmitEditComment(this)
$local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this)
handleSubmitEditComment= (elem) ->
url = Discussion.urlFor('update_comment', id)
body = Discussion.getWmdContent $content, $local, "comment-body-edit"
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: "json"
data: {body: body}
error: Discussion.formErrorHandler($local(".discussion-update-errors"))
success: (response, textStatus) ->
Discussion.clearFormErrors($local(".discussion-update-errors"))
$discussionContent.replaceWith(response.html)
Discussion.extendContentInfo response.content['id'], response['annotated_content_info']
Discussion.initializeContent($content)
Discussion.bindContentEvents($content)
handleEndorse = (elem, endorsed) ->
url = Discussion.urlFor('endorse_comment', id)
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: "json"
data: {endorsed: endorsed}
success: (response, textStatus) ->
if textStatus == "success"
if endorsed
$(content).addClass("endorsed")
else
$(content).removeClass("endorsed")
$(elem).unbind('click').click ->
handleEndorse(elem, !endorsed)
handleOpenClose = (elem, text) ->
url = Discussion.urlFor('openclose_thread', id)
closed = undefined
if text.match(/Close/)
closed = true
else if text.match(/[Oo]pen/)
closed = false
else
console.log "Unexpected text " + text + "for open/close thread."
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: "json"
data: {closed: closed}
success: (response, textStatus) =>
if textStatus == "success"
if closed
$(content).addClass("closed")
$(elem).text "Re-open Thread"
else
$(content).removeClass("closed")
$(elem).text "Close Thread"
error: (response, textStatus, e) ->
console.log e
handleDelete = (elem) ->
if $content.hasClass("thread")
url = Discussion.urlFor('delete_thread', id)
c = confirm "Are you sure to delete thread \"" + $content.find("a.thread-title").text() + "\"?"
else
url = Discussion.urlFor('delete_comment', id)
c = confirm "Are you sure to delete this comment? "
if c != true
return
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: "json"
data: {}
success: (response, textStatus) =>
if textStatus == "success"
$(content).remove()
error: (response, textStatus, e) ->
console.log e
handleHideSingleThread = (elem) ->
$threadTitle = $local(".thread-title")
$hideComments = $local(".discussion-hide-comments")
$hideComments.removeClass("discussion-hide-comments")
.addClass("discussion-show-comments")
$content.children(".comments").hide()
$threadTitle.unbind('click').click handleShowSingleThread
$hideComments.unbind('click').click handleShowSingleThread
prevHtml = $hideComments.html()
$hideComments.html prevHtml.replace "Hide", "Show"
handleShowSingleThread = ->
$threadTitle = $local(".thread-title")
$showComments = $local(".discussion-show-comments")
if not $showComments.hasClass("first-time") and (not $showComments.length or not $threadTitle.length)
return
rebindHideEvents = ->
$threadTitle.unbind('click').click handleHideSingleThread
$showComments.unbind('click').click handleHideSingleThread
$showComments.removeClass("discussion-show-comments")
.addClass("discussion-hide-comments")
prevHtml = $showComments.html()
$showComments.html prevHtml.replace "Show", "Hide"
if not $showComments.hasClass("first-time") and $content.children(".comments").length
$content.children(".comments").show()
rebindHideEvents()
else
discussion_id = $threadTitle.parents(".discussion").attr("_id")
url = Discussion.urlFor('retrieve_single_thread', discussion_id, id)
Discussion.safeAjax
$elem: $.merge($threadTitle, $showComments)
url: url
type: "GET"
dataType: 'json'
success: (response, textStatus) ->
Discussion.bulkExtendContentInfo response['annotated_content_info']
$content.append(response['html'])
$content.find(".comment").each (index, comment) ->
Discussion.initializeContent(comment)
Discussion.bindContentEvents(comment)
$showComments.removeClass("first-time")
rebindHideEvents()
Discussion.bindLocalEvents $local,
"click .thread-title": ->
handleShowSingleThread(this)
"click .discussion-show-comments": ->
handleShowSingleThread(this)
"click .discussion-hide-comments": ->
handleHideSingleThread(this)
"click .discussion-reply-thread": ->
handleShowSingleThread($local(".thread-title"))
handleReply(this)
"click .discussion-reply-comment": ->
handleReply(this)
"click .discussion-cancel-reply": ->
handleCancelReply(this)
"click .discussion-vote-up": ->
$elem = $(this)
if $elem.hasClass("voted")
handleUnvote($elem)
else
handleVote($elem, "up")
"click .discussion-vote-down": ->
$elem = $(this)
if $elem.hasClass("voted")
handleUnvote($elem)
else
handleVote($elem, "down")
"click .admin-endorse": ->
handleEndorse(this, not $content.hasClass("endorsed"))
"click .admin-openclose": ->
handleOpenClose(this, $(this).text())
"click .admin-edit": ->
if $content.hasClass("thread")
handleEditThread(this)
else
handleEditComment(this)
"click .admin-delete": ->
handleDelete(this)
initializeContent: (content) ->
unescapeHighlightTag = (text) ->
text.replace(/\&lt\;highlight\&gt\;/g, "<span class='search-highlight'>")
.replace(/\&lt\;\/highlight\&gt\;/g, "</span>")
stripHighlight = (text, type) ->
text.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "")
.replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "")
stripLatexHighlight = (text) ->
Discussion.processEachMathAndCode text, stripHighlight
markdownWithHighlight = (text) ->
converter = Markdown.getMathCompatibleConverter()
unescapeHighlightTag stripLatexHighlight converter.makeHtml text
$content = $(content)
initializeVote $content
if $content.hasClass("thread")
initializeFollowThread $content
$local = Discussion.generateLocal($content.children(".discussion-content"))
$local("span.timeago").timeago()
$contentTitle = $local(".thread-title")
if $contentTitle.length
$contentTitle.html unescapeHighlightTag stripLatexHighlight $contentTitle.html()
$contentBody = $local(".content-body")
$contentBody.html Discussion.postMathJaxProcessor markdownWithHighlight $contentBody.html()
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
id = $content.attr("_id")
if $content.hasClass("thread")
discussion_id = $content.attr("_discussion_id")
permalink = Discussion.urlFor("permanent_link_thread", discussion_id, id)
else
thread_id = $content.parents(".thread").attr("_id")
discussion_id = $content.parents(".thread").attr("_discussion_id")
permalink = Discussion.urlFor("permanent_link_comment", discussion_id, thread_id, id)
$local(".discussion-permanent-link").attr "href", permalink
if not Discussion.getContentInfo id, 'editable'
$local(".admin-edit").remove()
if not Discussion.getContentInfo id, 'can_reply'
$local(".discussion-reply").remove()
if not Discussion.getContentInfo id, 'can_endorse'
$local(".admin-endorse").remove()
if not Discussion.getContentInfo id, 'can_delete'
$local(".admin-delete").remove()
if not Discussion.getContentInfo id, 'can_openclose'
$local(".admin-openclose").remove()
#if not Discussion.getContentInfo id, 'can_vote'
# $local(".discussion-vote").css "visibility", "hidden"
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
initializeFollowDiscussion = (discussion) ->
$discussion = $(discussion)
id = $following.attr("_id")
$local = Discussion.generateLocal()
$discussion.children(".discussion-non-content")
.find(".discussion-title-wrapper")
.append(Discussion.subscriptionLink('discussion', id))
@Discussion = $.extend @Discussion,
initializeDiscussion: (discussion) ->
$discussion = $(discussion)
$discussion.find(".thread").each (index, thread) ->
Discussion.initializeContent(thread)
Discussion.bindContentEvents(thread)
$discussion.find(".comment").each (index, comment) ->
Discussion.initializeContent(comment)
Discussion.bindContentEvents(comment)
#initializeFollowDiscussion(discussion) TODO move this somewhere else
bindDiscussionEvents: (discussion) ->
$discussion = $(discussion)
$discussionNonContent = $discussion.children(".discussion-non-content")
$local = Discussion.generateLocal($discussion.children(".discussion-local"))
id = $discussion.attr("_id")
handleSubmitNewPost = (elem) ->
title = $local(".new-post-title").val()
body = Discussion.getWmdContent $discussion, $local, "new-post-body"
tags = $local(".new-post-tags").val()
url = Discussion.urlFor('create_thread', id)
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: 'json'
data:
title: title
body: body
tags: tags
error: Discussion.formErrorHandler($local(".new-post-form-errors"))
success: (response, textStatus) ->
Discussion.clearFormErrors($local(".new-post-form-errors"))
$thread = $(response.html)
$discussion.children(".threads").prepend($thread)
$local(".new-post-title").val("")
Discussion.setWmdContent $discussion, $local, "new-post-body", ""
$local(".new-post-tags").val("")
if $discussion.hasClass("inline-discussion")
$local(".new-post-form").addClass("collapsed")
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").hide()
handleCancelNewPost = (elem) ->
if $discussion.hasClass("inline-discussion")
$local(".new-post-form").addClass("collapsed")
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").hide()
handleSimilarPost = (elem) ->
$title = $local(".new-post-title")
$wrapper = $local(".new-post-similar-posts-wrapper")
$similarPosts = $local(".new-post-similar-posts")
prevText = $title.attr("prev-text")
text = $title.val()
if text == prevText
if $local(".similar-post").length
$wrapper.show()
else if $.trim(text).length
Discussion.safeAjax
$elem: $(elem)
url: Discussion.urlFor 'search_similar_threads', id
type: "GET"
dateType: 'json'
data:
text: $local(".new-post-title").val()
success: (response, textStatus) ->
$similarPosts.empty()
console.log response
if $.type(response) == "array" and response.length
$wrapper.show()
for thread in response
#singleThreadUrl = Discussion.urlFor 'retrieve_single_thread
$similarPost = $("<a>").addClass("similar-post")
.html(thread["title"])
.attr("href", "javascript:void(0)") #TODO
.appendTo($similarPosts)
else
$wrapper.hide()
else
$wrapper.hide()
$title.attr("prev-text", text)
initializeNewPost = ->
view = { discussion_id: id }
$discussionNonContent = $discussion.children(".discussion-non-content")
if not $local(".wmd-panel").length
$discussionNonContent.append Mustache.render Discussion.newPostTemplate, view
$newPostBody = $local(".new-post-body")
Discussion.makeWmdEditor $discussion, $local, "new-post-body"
$input = Discussion.getWmdInput($discussion, $local, "new-post-body")
$input.attr("placeholder", "post a new topic...")
if $discussion.hasClass("inline-discussion")
$input.bind 'focus', (e) ->
$local(".new-post-form").removeClass('collapsed')
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").removeClass('collapsed')
$local(".new-post-tags").tagsInput Discussion.tagsInputOptions()
$local(".new-post-title").blur ->
handleSimilarPost(this)
$local(".hide-similar-posts").click ->
$local(".new-post-similar-posts-wrapper").hide()
$local(".discussion-submit-post").click ->
handleSubmitNewPost(this)
$local(".discussion-cancel-post").click ->
handleCancelNewPost(this)
$local(".new-post-form").show()
handleAjaxReloadDiscussion = (elem, url) ->
if not url then return
$elem = $(elem)
$discussion = $elem.parents("section.discussion")
Discussion.safeAjax
$elem: $elem
url: url
type: "GET"
dataType: 'html'
success: (data, textStatus) ->
$data = $(data)
$parent = $discussion.parent()
$discussion.replaceWith($data)
$discussion = $parent.children(".discussion")
Discussion.initializeDiscussion($discussion)
Discussion.bindDiscussionEvents($discussion)
handleAjaxSearch = (elem) ->
$elem = $(elem)
url = URI($elem.attr("action")).addSearch({text: $local(".search-input").val()})
handleAjaxReloadDiscussion($elem, url)
handleAjaxSort = (elem) ->
$elem = $(elem)
url = $elem.attr("sort-url")
handleAjaxReloadDiscussion($elem, url)
handleAjaxPage = (elem) ->
$elem = $(elem)
url = $elem.attr("page-url")
handleAjaxReloadDiscussion($elem, url)
if $discussion.hasClass("inline-discussion")
initializeNewPost()
if $discussion.hasClass("forum-discussion")
$discussionSidebar = $(".discussion-sidebar")
if $discussionSidebar.length
$sidebarLocal = Discussion.generateLocal($discussionSidebar)
Discussion.bindLocalEvents $sidebarLocal,
"click .sidebar-new-post-button": (event) ->
initializeNewPost()
Discussion.bindLocalEvents $local,
"submit .search-wrapper>.discussion-search-form": (event) ->
event.preventDefault()
handleAjaxSearch(this)
"click .discussion-search-link": ->
handleAjaxSearch($local(".search-wrapper>.discussion-search-form"))
"click .discussion-sort-link": ->
handleAjaxSort(this)
$discussion.children(".discussion-paginator").find(".discussion-page-link").unbind('click').click ->
handleAjaxPage(this)
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
@Discussion = $.extend @Discussion,
initializeDiscussionModule: (elem) ->
$discussionModule = $(elem)
$local = Discussion.generateLocal($discussionModule)
handleShowDiscussion = (elem) ->
$elem = $(elem)
if not $local("section.discussion").length
discussion_id = $elem.attr("discussion_id")
url = Discussion.urlFor 'retrieve_discussion', discussion_id
Discussion.safeAjax
$elem: $elem
url: url
type: "GET"
success: (data, textStatus, xhr) ->
$discussionModule.append(data)
discussion = $local("section.discussion")
Discussion.initializeDiscussion(discussion)
Discussion.bindDiscussionEvents(discussion)
$elem.html("Hide Discussion")
$elem.unbind('click').click ->
handleHideDiscussion(this)
dataType: 'html'
else
$local("section.discussion").show()
$elem.html("Hide Discussion")
$elem.unbind('click').click ->
handleHideDiscussion(this)
handleHideDiscussion = (elem) ->
$local("section.discussion").hide()
$elem = $(elem)
$elem.html("Show Discussion")
$elem.unbind('click').click ->
handleShowDiscussion(this)
$local(".discussion-show").click ->
handleShowDiscussion(this)
$ ->
#toggle = ->
# $('.course-wrapper').toggleClass('closed')
#Discussion = window.Discussion
#if $('#accordion').length
# active = $('#accordion ul:has(li.active)').index('#accordion ul')
# $('#accordion').bind('accordionchange', @log).accordion
# active: if active >= 0 then active else 1
# header: 'h3'
# autoHeight: false
# $('#open_close_accordion a').click toggle
# $('#accordion').show()
#$(".discussion-module").each (index, elem) ->
# Discussion.initializeDiscussionModule(elem)
#$("section.discussion").each (index, discussion) ->
# Discussion.initializeDiscussion(discussion)
# Discussion.bindDiscussionEvents(discussion)
#Discussion.initializeUserProfile($(".discussion-sidebar>.user-profile"))
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
@Discussion = $.extend @Discussion,
newPostTemplate: """
<form class="new-post-form collapsed" id="new-post-form" style="display: block; ">
<ul class="new-post-form-errors discussion-errors"></ul>
<input type="text" class="new-post-title title-input" placeholder="Title" />
<div class="new-post-similar-posts-wrapper" style="display: none">
Similar Posts:
<a class="hide-similar-posts" href="javascript:void(0)">Hide</a>
<div class="new-post-similar-posts"></div>
</div>
<div class="new-post-body reply-body"></div>
<input class="new-post-tags" placeholder="Tags" />
<div class="post-options">
<input type="checkbox" class="discussion-post-anonymously" id="discussion-post-anonymously-${discussion_id}">
<label for="discussion-post-anonymously-${discussion_id}">post anonymously</label>
<input type="checkbox" class="discussion-auto-watch" id="discussion-autowatch-${discussion_id}" checked="">
<label for="discussion-auto-watch-${discussion_id}">follow this thread</label>
</div>
<div class="new-post-control post-control">
<a class="discussion-cancel-post" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-post control-button" href="javascript:void(0)">Submit</a>
</div>
</form>
"""
replyTemplate: """
<form class="discussion-reply-new">
<ul class="discussion-errors"></ul>
<div class="reply-body"></div>
<input type="checkbox" class="discussion-post-anonymously" id="discussion-post-anonymously-{{id}}" />
<label for="discussion-post-anonymously-{{id}}">post anonymously</label>
{{#showWatchCheckbox}}
<input type="checkbox" class="discussion-auto-watch" id="discussion-autowatch-{{id}}" checked />
<label for="discussion-auto-watch-{{id}}">follow this thread</label>
{{/showWatchCheckbox}}
<br />
<div class = "reply-post-control">
<a class="discussion-cancel-post" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-post control-button" href="javascript:void(0)">Submit</a>
</div>
</form>
"""
editThreadTemplate: """
<form class="discussion-content-edit discussion-thread-edit" _id="{{id}}">
<ul class="discussion-errors discussion-update-errors"></ul>
<input type="text" class="thread-title-edit title-input" placeholder="Title" value="{{title}}"/>
<div class="thread-body-edit body-input">{{body}}</div>
<input class="thread-tags-edit" placeholder="Tags" value="{{tags}}" />
<div class = "edit-post-control">
<a class="discussion-cancel-update" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-update control-button" href="javascript:void(0)">Update</a>
</div>
</form>
"""
editCommentTemplate: """
<form class="discussion-content-edit discussion-comment-edit" _id="{{id}}">
<ul class="discussion-errors discussion-update-errors"></ul>
<div class="comment-body-edit body-input">{{body}}</div>
<div class = "edit-post-control">
<a class="discussion-cancel-update" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-update control-button" href="javascript:void(0)">Update</a>
</div>
</form>
"""
class @DiscussionUtil if not @Discussion?
@Discussion = {}
@wmdEditors: {} Discussion = @Discussion
@getTemplate: (id) -> wmdEditors = {}
$("script##{id}").html()
@getDiscussionData: (id) -> @Discussion = $.extend @Discussion,
return $$discussion_data[id]
@addContent: (id, content) -> window.$$contents[id] = content generateLocal: (elem) ->
(selector) -> $(elem).find(selector)
@getContent: (id) -> window.$$contents[id] generateDiscussionLink: (cls, txt, handler) ->
@addDiscussion: (id, discussion) -> window.$$discussions[id] = discussion
@getDiscussion: (id) -> window.$$discussions[id]
@bulkUpdateContentInfo: (infos) ->
for id, info of infos
@getContent(id).updateInfo(info)
@generateDiscussionLink: (cls, txt, handler) ->
$("<a>").addClass("discussion-link") $("<a>").addClass("discussion-link")
.attr("href", "javascript:void(0)") .attr("href", "javascript:void(0)")
.addClass(cls).html(txt) .addClass(cls).html(txt)
.click -> handler(this) .click -> handler(this)
@urlFor: (name, param, param1, param2) -> urlFor: (name, param, param1, param2) ->
{ {
follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow" follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow"
unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow" unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow"
...@@ -58,7 +48,7 @@ class @DiscussionUtil ...@@ -58,7 +48,7 @@ class @DiscussionUtil
permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}" permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}"
}[name] }[name]
@safeAjax: (params) -> safeAjax: (params) ->
$elem = params.$elem $elem = params.$elem
if $elem.attr("disabled") if $elem.attr("disabled")
return return
...@@ -66,31 +56,17 @@ class @DiscussionUtil ...@@ -66,31 +56,17 @@ class @DiscussionUtil
$.ajax(params).always -> $.ajax(params).always ->
$elem.removeAttr("disabled") $elem.removeAttr("disabled")
@get: ($elem, url, data, success) -> handleAnchorAndReload: (response) ->
@safeAjax #window.location = window.location.pathname + "#" + response['id']
$elem: $elem window.location.reload()
url: url
type: "GET"
dataType: "json"
data: data
success: success
@post: ($elem, url, data, success) ->
@safeAjax
$elem: $elem
url: url
type: "POST"
dataType: "json"
data: data
success: success
@bindLocalEvents: ($local, eventsHandler) -> bindLocalEvents: ($local, eventsHandler) ->
for eventSelector, handler of eventsHandler for eventSelector, handler of eventsHandler
[event, selector] = eventSelector.split(' ') [event, selector] = eventSelector.split(' ')
$local(selector).unbind(event)[event] handler $local(selector).unbind(event)[event] handler
@tagsInputOptions: -> tagsInputOptions: ->
autocomplete_url: @urlFor('tags_autocomplete') autocomplete_url: Discussion.urlFor('tags_autocomplete')
autocomplete: autocomplete:
remoteDataType: 'json' remoteDataType: 'json'
interactive: true interactive: true
...@@ -99,7 +75,23 @@ class @DiscussionUtil ...@@ -99,7 +75,23 @@ class @DiscussionUtil
defaultText: "Tag your post: press enter after each tag" defaultText: "Tag your post: press enter after each tag"
removeWithBackspace: true removeWithBackspace: true
@formErrorHandler: (errorsField) -> isSubscribed: (id, type) ->
$$user_info? and (
if type == "thread"
id in $$user_info.subscribed_thread_ids
else if type == "commentable" or type == "discussion"
id in $$user_info.subscribed_commentable_ids
else
id in $$user_info.subscribed_user_ids
)
isUpvoted: (id) ->
$$user_info? and (id in $$user_info.upvoted_ids)
isDownvoted: (id) ->
$$user_info? and (id in $$user_info.downvoted_ids)
formErrorHandler: (errorsField) ->
(xhr, textStatus, error) -> (xhr, textStatus, error) ->
response = JSON.parse(xhr.responseText) response = JSON.parse(xhr.responseText)
if response.errors? and response.errors.length > 0 if response.errors? and response.errors.length > 0
...@@ -107,13 +99,13 @@ class @DiscussionUtil ...@@ -107,13 +99,13 @@ class @DiscussionUtil
for error in response.errors for error in response.errors
errorsField.append($("<li>").addClass("new-post-form-error").html(error)) errorsField.append($("<li>").addClass("new-post-form-error").html(error))
@clearFormErrors: (errorsField) -> clearFormErrors: (errorsField) ->
errorsField.empty() errorsField.empty()
@postMathJaxProcessor: (text) -> postMathJaxProcessor: (text) ->
RE_INLINEMATH = /^\$([^\$]*)\$/g RE_INLINEMATH = /^\$([^\$]*)\$/g
RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g
@processEachMathAndCode text, (s, type) -> Discussion.processEachMathAndCode text, (s, type) ->
if type == 'display' if type == 'display'
s.replace RE_DISPLAYMATH, ($0, $1) -> s.replace RE_DISPLAYMATH, ($0, $1) ->
"\\[" + $1 + "\\]" "\\[" + $1 + "\\]"
...@@ -123,43 +115,61 @@ class @DiscussionUtil ...@@ -123,43 +115,61 @@ class @DiscussionUtil
else else
s s
@makeWmdEditor: ($content, $local, cls_identifier) -> makeWmdEditor: ($content, $local, cls_identifier) ->
elem = $local(".#{cls_identifier}") elem = $local(".#{cls_identifier}")
id = $content.attr("_id") id = $content.attr("_id")
appended_id = "-#{cls_identifier}-#{id}" appended_id = "-#{cls_identifier}-#{id}"
imageUploadUrl = @urlFor('upload') imageUploadUrl = Discussion.urlFor('upload')
_processor = (_this) -> editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, Discussion.postMathJaxProcessor
(text) -> _this.postMathJaxProcessor(text) wmdEditors["#{cls_identifier}-#{id}"] = editor
editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, _processor(@)
@wmdEditors["#{cls_identifier}-#{id}"] = editor
editor editor
@getWmdEditor: ($content, $local, cls_identifier) -> getWmdEditor: ($content, $local, cls_identifier) ->
id = $content.attr("_id") id = $content.attr("_id")
@wmdEditors["#{cls_identifier}-#{id}"] wmdEditors["#{cls_identifier}-#{id}"]
@getWmdInput: ($content, $local, cls_identifier) -> getWmdInput: ($content, $local, cls_identifier) ->
id = $content.attr("_id") id = $content.attr("_id")
$local("#wmd-input-#{cls_identifier}-#{id}") $local("#wmd-input-#{cls_identifier}-#{id}")
@getWmdContent: ($content, $local, cls_identifier) -> getWmdContent: ($content, $local, cls_identifier) ->
@getWmdInput($content, $local, cls_identifier).val() Discussion.getWmdInput($content, $local, cls_identifier).val()
@setWmdContent: ($content, $local, cls_identifier, text) -> setWmdContent: ($content, $local, cls_identifier, text) ->
@getWmdInput($content, $local, cls_identifier).val(text) Discussion.getWmdInput($content, $local, cls_identifier).val(text)
@getWmdEditor($content, $local, cls_identifier).refreshPreview() Discussion.getWmdEditor($content, $local, cls_identifier).refreshPreview()
@subscriptionLink: (type, id) -> getContentInfo: (id, attr) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
(window.$$annotated_content_info[id] || {})[attr]
setContentInfo: (id, attr, value) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info[id] ||= {}
window.$$annotated_content_info[id][attr] = value
extendContentInfo: (id, newInfo) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info[id] = newInfo
bulkExtendContentInfo: (newInfos) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info = $.extend window.$$annotated_content_info, newInfos
subscriptionLink: (type, id) ->
followLink = -> followLink = ->
@generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow) Discussion.generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow)
unfollowLink = -> unfollowLink = ->
@generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow) Discussion.generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow)
handleFollow = (elem) -> handleFollow = (elem) ->
@safeAjax Discussion.safeAjax
$elem: $(elem) $elem: $(elem)
url: @urlFor("follow_#{type}", id) url: Discussion.urlFor("follow_#{type}", id)
type: "POST" type: "POST"
success: (response, textStatus) -> success: (response, textStatus) ->
if textStatus == "success" if textStatus == "success"
...@@ -167,21 +177,21 @@ class @DiscussionUtil ...@@ -167,21 +177,21 @@ class @DiscussionUtil
dataType: 'json' dataType: 'json'
handleUnfollow = (elem) -> handleUnfollow = (elem) ->
@safeAjax Discussion.safeAjax
$elem: $(elem) $elem: $(elem)
url: @urlFor("unfollow_#{type}", id) url: Discussion.urlFor("unfollow_#{type}", id)
type: "POST" type: "POST"
success: (response, textStatus) -> success: (response, textStatus) ->
if textStatus == "success" if textStatus == "success"
$(elem).replaceWith followLink() $(elem).replaceWith followLink()
dataType: 'json' dataType: 'json'
if @isSubscribed(id, type) if Discussion.isSubscribed(id, type)
unfollowLink() unfollowLink()
else else
followLink() followLink()
@processEachMathAndCode: (text, processor) -> processEachMathAndCode: (text, processor) ->
codeArchive = [] codeArchive = []
...@@ -232,18 +242,3 @@ class @DiscussionUtil ...@@ -232,18 +242,3 @@ class @DiscussionUtil
text = $div.html() text = $div.html()
text text
@unescapeHighlightTag: (text) ->
text.replace(/\&lt\;highlight\&gt\;/g, "<span class='search-highlight'>")
.replace(/\&lt\;\/highlight\&gt\;/g, "</span>")
@stripHighlight: (text) ->
text.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "")
.replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "")
@stripLatexHighlight: (text) ->
@processEachMathAndCode text, @stripHighlight
@markdownWithHighlight: (text) ->
converter = Markdown.getMathCompatibleConverter()
@unescapeHighlightTag @stripLatexHighlight converter.makeHtml text
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment