diff --git a/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee
index 85ab5ec254..71495ad9c6 100644
--- a/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee
+++ b/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee
@@ -1,13 +1,12 @@
describe "DiscussionContentView", ->
beforeEach ->
- setFixtures
- (
+ setFixtures(
"""
-
- + 0
+
+ 0 votes (click to vote)
Post Title
robot
@@ -23,16 +22,21 @@ describe "DiscussionContentView", ->
"""
)
- @thread = new Thread {
- id: '01234567',
- user_id: '567',
- course_id: 'mitX/999/test',
- body: 'this is a thread',
- created_at: '2013-04-03T20:08:39Z',
- abuse_flaggers: ['123']
- roles: []
+ @threadData = {
+ id: '01234567',
+ user_id: '567',
+ course_id: 'mitX/999/test',
+ body: 'this is a thread',
+ created_at: '2013-04-03T20:08:39Z',
+ abuse_flaggers: ['123'],
+ votes: {up_count: '42'},
+ type: "thread",
+ roles: []
}
+ @thread = new Thread(@threadData)
@view = new DiscussionContentView({ model: @thread })
+ @view.setElement($('.discussion-post'))
+ window.user = new DiscussionUser({id: '567', upvoted_ids: []})
it 'defines the tag', ->
expect($('#jasmine-fixtures')).toExist
@@ -56,3 +60,15 @@ describe "DiscussionContentView", ->
@thread.set("abuse_flaggers",temp_array)
@thread.unflagAbuse()
expect(@thread.get 'abuse_flaggers').toEqual []
+
+ it 'renders the vote button properly', ->
+ DiscussionViewSpecHelper.checkRenderVote(@view, @thread)
+
+ it 'votes correctly', ->
+ DiscussionViewSpecHelper.checkVote(@view, @thread, @threadData, false)
+
+ it 'unvotes correctly', ->
+ DiscussionViewSpecHelper.checkUnvote(@view, @thread, @threadData, false)
+
+ it 'toggles the vote correctly', ->
+ DiscussionViewSpecHelper.checkToggleVote(@view, @thread)
diff --git a/common/static/coffee/spec/discussion/view/discussion_thread_profile_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_thread_profile_view_spec.coffee
new file mode 100644
index 0000000000..f10d30d0af
--- /dev/null
+++ b/common/static/coffee/spec/discussion/view/discussion_thread_profile_view_spec.coffee
@@ -0,0 +1,40 @@
+describe "DiscussionThreadProfileView", ->
+ beforeEach ->
+ setFixtures(
+ """
+
+ """
+ )
+
+ @threadData = {
+ id: "dummy",
+ user_id: "567",
+ course_id: "TestOrg/TestCourse/TestRun",
+ body: "this is a thread",
+ created_at: "2013-04-03T20:08:39Z",
+ abuse_flaggers: [],
+ votes: {up_count: "42"}
+ }
+ @thread = new Thread(@threadData)
+ @view = new DiscussionThreadProfileView({ model: @thread })
+ @view.setElement($(".discussion-post"))
+ window.user = new DiscussionUser({id: "567", upvoted_ids: []})
+
+ it "renders the vote correctly", ->
+ DiscussionViewSpecHelper.checkRenderVote(@view, @thread)
+
+ it "votes correctly", ->
+ DiscussionViewSpecHelper.checkVote(@view, @thread, @threadData, true)
+
+ it "unvotes correctly", ->
+ DiscussionViewSpecHelper.checkUnvote(@view, @thread, @threadData, true)
+
+ it "toggles the vote correctly", ->
+ DiscussionViewSpecHelper.checkToggleVote(@view, @thread)
+
+ it "vote button activates on appropriate events", ->
+ DiscussionViewSpecHelper.checkVoteButtonEvents(@view)
diff --git a/common/static/coffee/spec/discussion/view/discussion_thread_show_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_thread_show_view_spec.coffee
new file mode 100644
index 0000000000..69e4b231a2
--- /dev/null
+++ b/common/static/coffee/spec/discussion/view/discussion_thread_show_view_spec.coffee
@@ -0,0 +1,40 @@
+describe "DiscussionThreadShowView", ->
+ beforeEach ->
+ setFixtures(
+ """
+
+ """
+ )
+
+ @threadData = {
+ id: "dummy",
+ user_id: "567",
+ course_id: "TestOrg/TestCourse/TestRun",
+ body: "this is a thread",
+ created_at: "2013-04-03T20:08:39Z",
+ abuse_flaggers: [],
+ votes: {up_count: "42"}
+ }
+ @thread = new Thread(@threadData)
+ @view = new DiscussionThreadShowView({ model: @thread })
+ @view.setElement($(".discussion-post"))
+ window.user = new DiscussionUser({id: "567", upvoted_ids: []})
+
+ it "renders the vote correctly", ->
+ DiscussionViewSpecHelper.checkRenderVote(@view, @thread)
+
+ it "votes correctly", ->
+ DiscussionViewSpecHelper.checkVote(@view, @thread, @threadData, true)
+
+ it "unvotes correctly", ->
+ DiscussionViewSpecHelper.checkUnvote(@view, @thread, @threadData, true)
+
+ it 'toggles the vote correctly', ->
+ DiscussionViewSpecHelper.checkToggleVote(@view, @thread)
+
+ it "vote button activates on appropriate events", ->
+ DiscussionViewSpecHelper.checkVoteButtonEvents(@view)
diff --git a/common/static/coffee/spec/discussion/view/discussion_view_spec_helper.coffee b/common/static/coffee/spec/discussion/view/discussion_view_spec_helper.coffee
new file mode 100644
index 0000000000..d5c25aa5e2
--- /dev/null
+++ b/common/static/coffee/spec/discussion/view/discussion_view_spec_helper.coffee
@@ -0,0 +1,113 @@
+class @DiscussionViewSpecHelper
+ @expectVoteRendered = (view, voted) ->
+ button = view.$el.find(".vote-btn")
+ if voted
+ expect(button.hasClass("is-cast")).toBe(true)
+ expect(button.attr("aria-pressed")).toEqual("true")
+ expect(button.attr("data-tooltip")).toEqual("remove vote")
+ expect(button.find(".votes-count-number").html()).toEqual("43")
+ expect(button.find(".sr").html()).toEqual("votes (click to remove your vote)")
+ else
+ expect(button.hasClass("is-cast")).toBe(false)
+ expect(button.attr("aria-pressed")).toEqual("false")
+ expect(button.attr("data-tooltip")).toEqual("vote")
+ expect(button.find(".votes-count-number").html()).toEqual("42")
+ expect(button.find(".sr").html()).toEqual("votes (click to vote)")
+
+ @checkRenderVote = (view, model) ->
+ view.renderVote()
+ DiscussionViewSpecHelper.expectVoteRendered(view, false)
+ window.user.vote(model)
+ view.renderVote()
+ DiscussionViewSpecHelper.expectVoteRendered(view, true)
+ window.user.unvote(model)
+ view.renderVote()
+ DiscussionViewSpecHelper.expectVoteRendered(view, false)
+
+ @checkVote = (view, model, modelData, checkRendering) ->
+ view.renderVote()
+ if checkRendering
+ DiscussionViewSpecHelper.expectVoteRendered(view, false)
+
+ spyOn($, "ajax").andCallFake((params) =>
+ newModelData = {}
+ $.extend(newModelData, modelData, {votes: {up_count: "43"}})
+ params.success(newModelData, "success")
+ # Caller invokes always function on return value but it doesn't matter here
+ {always: ->}
+ )
+
+ view.vote()
+ expect(window.user.voted(model)).toBe(true)
+ if checkRendering
+ DiscussionViewSpecHelper.expectVoteRendered(view, true)
+ expect($.ajax).toHaveBeenCalled()
+ $.ajax.reset()
+
+ # Check idempotence
+ view.vote()
+ expect(window.user.voted(model)).toBe(true)
+ if checkRendering
+ DiscussionViewSpecHelper.expectVoteRendered(view, true)
+ expect($.ajax).toHaveBeenCalled()
+
+ @checkUnvote = (view, model, modelData, checkRendering) ->
+ window.user.vote(model)
+ expect(window.user.voted(model)).toBe(true)
+ if checkRendering
+ DiscussionViewSpecHelper.expectVoteRendered(view, true)
+
+ spyOn($, "ajax").andCallFake((params) =>
+ newModelData = {}
+ $.extend(newModelData, modelData, {votes: {up_count: "42"}})
+ params.success(newModelData, "success")
+ # Caller invokes always function on return value but it doesn't matter here
+ {always: ->}
+ )
+
+ view.unvote()
+ expect(window.user.voted(model)).toBe(false)
+ if checkRendering
+ DiscussionViewSpecHelper.expectVoteRendered(view, false)
+ expect($.ajax).toHaveBeenCalled()
+ $.ajax.reset()
+
+ # Check idempotence
+ view.unvote()
+ expect(window.user.voted(model)).toBe(false)
+ if checkRendering
+ DiscussionViewSpecHelper.expectVoteRendered(view, false)
+ expect($.ajax).toHaveBeenCalled()
+
+ @checkToggleVote = (view, model) ->
+ event = {preventDefault: ->}
+ spyOn(event, "preventDefault")
+ spyOn(view, "vote").andCallFake(() -> window.user.vote(model))
+ spyOn(view, "unvote").andCallFake(() -> window.user.unvote(model))
+
+ expect(window.user.voted(model)).toBe(false)
+ view.toggleVote(event)
+ expect(view.vote).toHaveBeenCalled()
+ expect(view.unvote).not.toHaveBeenCalled()
+ expect(event.preventDefault.callCount).toEqual(1)
+
+ view.vote.reset()
+ view.unvote.reset()
+ expect(window.user.voted(model)).toBe(true)
+ view.toggleVote(event)
+ expect(view.vote).not.toHaveBeenCalled()
+ expect(view.unvote).toHaveBeenCalled()
+ expect(event.preventDefault.callCount).toEqual(2)
+
+ @checkVoteButtonEvents = (view) ->
+ spyOn(view, "toggleVote")
+ button = view.$el.find(".vote-btn")
+
+ button.click()
+ expect(view.toggleVote).toHaveBeenCalled()
+ view.toggleVote.reset()
+ button.trigger($.Event("keydown", {which: 13}))
+ expect(view.toggleVote).toHaveBeenCalled()
+ view.toggleVote.reset()
+ button.trigger($.Event("keydown", {which: 32}))
+ expect(view.toggleVote).not.toHaveBeenCalled()
diff --git a/common/static/coffee/spec/discussion/view/thread_response_show_view_spec.coffee b/common/static/coffee/spec/discussion/view/thread_response_show_view_spec.coffee
new file mode 100644
index 0000000000..7ba00c66d1
--- /dev/null
+++ b/common/static/coffee/spec/discussion/view/thread_response_show_view_spec.coffee
@@ -0,0 +1,40 @@
+describe "ThreadResponseShowView", ->
+ beforeEach ->
+ setFixtures(
+ """
+
+ """
+ )
+
+ @commentData = {
+ id: "dummy",
+ user_id: "567",
+ course_id: "TestOrg/TestCourse/TestRun",
+ body: "this is a comment",
+ created_at: "2013-04-03T20:08:39Z",
+ abuse_flaggers: [],
+ votes: {up_count: "42"}
+ }
+ @comment = new Comment(@commentData)
+ @view = new ThreadResponseShowView({ model: @comment })
+ @view.setElement($(".discussion-post"))
+ window.user = new DiscussionUser({id: "567", upvoted_ids: []})
+
+ it "renders the vote correctly", ->
+ DiscussionViewSpecHelper.checkRenderVote(@view, @comment)
+
+ it "votes correctly", ->
+ DiscussionViewSpecHelper.checkVote(@view, @comment, @commentData, true)
+
+ it "unvotes correctly", ->
+ DiscussionViewSpecHelper.checkUnvote(@view, @comment, @commentData, true)
+
+ it 'toggles the vote correctly', ->
+ DiscussionViewSpecHelper.checkToggleVote(@view, @comment)
+
+ it "vote button activates on appropriate events", ->
+ DiscussionViewSpecHelper.checkVoteButtonEvents(@view)
diff --git a/common/static/coffee/src/discussion/content.coffee b/common/static/coffee/src/discussion/content.coffee
index 23a31ae7e6..5e3d4ce20b 100644
--- a/common/static/coffee/src/discussion/content.coffee
+++ b/common/static/coffee/src/discussion/content.coffee
@@ -99,6 +99,13 @@ if Backbone?
@get("abuse_flaggers").pop(window.user.get('id'))
@trigger "change", @
+ vote: ->
+ @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) + 1
+ @trigger "change", @
+
+ unvote: ->
+ @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) - 1
+ @trigger "change", @
class @Thread extends @Content
urlMappers:
@@ -130,14 +137,6 @@ if Backbone?
unfollow: ->
@set('subscribed', false)
- vote: ->
- @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) + 1
- @trigger "change", @
-
- unvote: ->
- @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) - 1
- @trigger "change", @
-
display_body: ->
if @has("highlighted_body")
String(@get("highlighted_body")).replace(//g, '').replace(/<\/highlight>/g, '')
diff --git a/common/static/coffee/src/discussion/utils.coffee b/common/static/coffee/src/discussion/utils.coffee
index a85e4f0eaa..0e8362472a 100644
--- a/common/static/coffee/src/discussion/utils.coffee
+++ b/common/static/coffee/src/discussion/utils.coffee
@@ -91,7 +91,7 @@ class @DiscussionUtil
@activateOnEnter: (event, func) ->
if event.which == 13
- e.preventDefault()
+ event.preventDefault()
func(event)
@makeFocusTrap: (elem) ->
diff --git a/common/static/coffee/src/discussion/views/discussion_content_view.coffee b/common/static/coffee/src/discussion/views/discussion_content_view.coffee
index 9c3c4a01f5..96d74df4ef 100644
--- a/common/static/coffee/src/discussion/views/discussion_content_view.coffee
+++ b/common/static/coffee/src/discussion/views/discussion_content_view.coffee
@@ -159,3 +159,42 @@ if Backbone?
temp_array = []
@model.set('abuse_flaggers', temp_array)
+
+ renderVote: =>
+ button = @$el.find(".vote-btn")
+ voted = window.user.voted(@model)
+ voteNum = @model.get("votes")["up_count"]
+ button.toggleClass("is-cast", voted)
+ button.attr("aria-pressed", voted)
+ button.attr("data-tooltip", if voted then "remove vote" else "vote")
+ button.find(".votes-count-number").html(voteNum)
+ button.find(".sr").html(if voted then "votes (click to remove your vote)" else "votes (click to vote)")
+
+ toggleVote: (event) =>
+ event.preventDefault()
+ if window.user.voted(@model)
+ @unvote()
+ else
+ @vote()
+
+ vote: =>
+ window.user.vote(@model)
+ url = @model.urlFor("upvote")
+ DiscussionUtil.safeAjax
+ $elem: @$el.find(".vote-btn")
+ url: url
+ type: "POST"
+ success: (response, textStatus) =>
+ if textStatus == 'success'
+ @model.set(response)
+
+ unvote: =>
+ window.user.unvote(@model)
+ url = @model.urlFor("unvote")
+ DiscussionUtil.safeAjax
+ $elem: @$el.find(".vote-btn")
+ url: url
+ type: "POST"
+ success: (response, textStatus) =>
+ if textStatus == 'success'
+ @model.set(response)
diff --git a/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee b/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee
index 7130ac555c..f6a6ea8eb6 100644
--- a/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee
+++ b/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee
@@ -2,7 +2,10 @@ if Backbone?
class @DiscussionThreadProfileView extends DiscussionContentView
expanded = false
events:
- "click .discussion-vote": "toggleVote"
+ "click .vote-btn":
+ (event) -> @toggleVote(event)
+ "keydown .vote-btn":
+ (event) -> DiscussionUtil.activateOnEnter(event, @toggleVote)
"click .action-follow": "toggleFollowing"
"keypress .action-follow":
(event) -> DiscussionUtil.activateOnEnter(event, toggleFollowing)
@@ -27,7 +30,7 @@ if Backbone?
@$el.html(Mustache.render(@template, params))
@initLocal()
@delegateEvents()
- @renderVoted()
+ @renderVote()
@renderAttrs()
@$("span.timeago").timeago()
@convertMath()
@@ -35,15 +38,8 @@ if Backbone?
@renderResponses()
@
- renderVoted: =>
- if window.user.voted(@model)
- @$("[data-role=discussion-vote]").addClass("is-cast")
- else
- @$("[data-role=discussion-vote]").removeClass("is-cast")
-
updateModelDetails: =>
- @renderVoted()
- @$("[data-role=discussion-vote] .votes-count-number").html(@model.get("votes")["up_count"])
+ @renderVote()
convertMath: ->
element = @$(".post-body")
@@ -71,35 +67,6 @@ if Backbone?
addComment: =>
@model.comment()
- toggleVote: (event) ->
- event.preventDefault()
- if window.user.voted(@model)
- @unvote()
- else
- @vote()
-
- vote: ->
- window.user.vote(@model)
- url = @model.urlFor("upvote")
- DiscussionUtil.safeAjax
- $elem: @$(".discussion-vote")
- url: url
- type: "POST"
- success: (response, textStatus) =>
- if textStatus == 'success'
- @model.set(response)
-
- unvote: ->
- window.user.unvote(@model)
- url = @model.urlFor("unvote")
- DiscussionUtil.safeAjax
- $elem: @$(".discussion-vote")
- url: url
- type: "POST"
- success: (response, textStatus) =>
- if textStatus == 'success'
- @model.set(response)
-
edit: ->
abbreviateBody: ->
diff --git a/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee b/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee
index 1a3f8929e1..14dd01e3fa 100644
--- a/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee
+++ b/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee
@@ -2,7 +2,10 @@ if Backbone?
class @DiscussionThreadShowView extends DiscussionContentView
events:
- "click .discussion-vote": "toggleVote"
+ "click .vote-btn":
+ (event) -> @toggleVote(event)
+ "keydown .vote-btn":
+ (event) -> DiscussionUtil.activateOnEnter(event, @toggleVote)
"click .discussion-flag-abuse": "toggleFlagAbuse"
"keypress .discussion-flag-abuse":
(event) -> DiscussionUtil.activateOnEnter(event, toggleFlagAbuse)
@@ -28,7 +31,7 @@ if Backbone?
render: ->
@$el.html(@renderTemplate())
@delegateEvents()
- @renderVoted()
+ @renderVote()
@renderFlagged()
@renderPinned()
@renderAttrs()
@@ -38,14 +41,6 @@ if Backbone?
@highlight @$("h1,h3")
@
- renderVoted: =>
- if window.user.voted(@model)
- @$("[data-role=discussion-vote]").addClass("is-cast")
- @$("[data-role=discussion-vote] span.sr").html("votes (click to remove your vote)")
- else
- @$("[data-role=discussion-vote]").removeClass("is-cast")
- @$("[data-role=discussion-vote] span.sr").html("votes (click to vote)")
-
renderFlagged: =>
if window.user.id in @model.get("abuse_flaggers") or (DiscussionUtil.isFlagModerator and @model.get("abuse_flaggers").length > 0)
@$("[data-role=thread-flag]").addClass("flagged")
@@ -70,52 +65,15 @@ if Backbone?
updateModelDetails: =>
- @renderVoted()
+ @renderVote()
@renderFlagged()
@renderPinned()
- @$("[data-role=discussion-vote] .votes-count-number").html(@model.get("votes")["up_count"] + '')
- if window.user.voted(@model)
- @$("[data-role=discussion-vote] .votes-count-number span.sr").html("votes (click to remove your vote)")
- else
- @$("[data-role=discussion-vote] .votes-count-number span.sr").html("votes (click to vote)")
-
convertMath: ->
element = @$(".post-body")
element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.text()
MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]]
- toggleVote: (event) ->
- event.preventDefault()
- if window.user.voted(@model)
- @unvote()
- else
- @vote()
-
- vote: ->
- window.user.vote(@model)
- url = @model.urlFor("upvote")
- DiscussionUtil.safeAjax
- $elem: @$(".discussion-vote")
- url: url
- type: "POST"
- success: (response, textStatus) =>
- if textStatus == 'success'
- @model.set(response, {silent: true})
-
-
- unvote: ->
- window.user.unvote(@model)
- url = @model.urlFor("unvote")
- DiscussionUtil.safeAjax
- $elem: @$(".discussion-vote")
- url: url
- type: "POST"
- success: (response, textStatus) =>
- if textStatus == 'success'
- @model.set(response, {silent: true})
-
-
edit: (event) ->
@trigger "thread:edit", event
diff --git a/common/static/coffee/src/discussion/views/thread_response_show_view.coffee b/common/static/coffee/src/discussion/views/thread_response_show_view.coffee
index eaed0568c2..57736e789d 100644
--- a/common/static/coffee/src/discussion/views/thread_response_show_view.coffee
+++ b/common/static/coffee/src/discussion/views/thread_response_show_view.coffee
@@ -1,7 +1,10 @@
if Backbone?
class @ThreadResponseShowView extends DiscussionContentView
events:
- "click .vote-btn": "toggleVote"
+ "click .vote-btn":
+ (event) -> @toggleVote(event)
+ "keydown .vote-btn":
+ (event) -> DiscussionUtil.activateOnEnter(event, @toggleVote)
"click .action-endorse": "toggleEndorse"
"click .action-delete": "_delete"
"click .action-edit": "edit"
@@ -23,9 +26,7 @@ if Backbone?
render: ->
@$el.html(@renderTemplate())
@delegateEvents()
- if window.user.voted(@model)
- @$(".vote-btn").addClass("is-cast")
- @$(".vote-btn span.sr").html("votes (click to remove your vote)")
+ @renderVote()
@renderAttrs()
@renderFlagged()
@$el.find(".posted-details").timeago()
@@ -46,39 +47,6 @@ if Backbone?
@$el.addClass("community-ta")
@$el.prepend('')
- toggleVote: (event) ->
- event.preventDefault()
- @$(".vote-btn").toggleClass("is-cast")
- if @$(".vote-btn").hasClass("is-cast")
- @vote()
- @$(".vote-btn span.sr").html("votes (click to remove your vote)")
- else
- @unvote()
- @$(".vote-btn span.sr").html("votes (click to vote)")
-
- vote: ->
- url = @model.urlFor("upvote")
- @$(".votes-count-number").html((parseInt(@$(".votes-count-number").html()) + 1) + '')
- DiscussionUtil.safeAjax
- $elem: @$(".discussion-vote")
- url: url
- type: "POST"
- success: (response, textStatus) =>
- if textStatus == 'success'
- @model.set(response)
-
- unvote: ->
- url = @model.urlFor("unvote")
- @$(".votes-count-number").html((parseInt(@$(".votes-count-number").html()) - 1)+'')
- DiscussionUtil.safeAjax
- $elem: @$(".discussion-vote")
- url: url
- type: "POST"
- success: (response, textStatus) =>
- if textStatus == 'success'
- @model.set(response)
-
-
edit: (event) ->
@trigger "response:edit", event
@@ -115,4 +83,5 @@ if Backbone?
@$(".discussion-flag-abuse .flag-label").html("Report Misuse")
updateModelDetails: =>
+ @renderVote()
@renderFlagged()
diff --git a/common/static/js_test.yml b/common/static/js_test.yml
index 31929401f0..c56e3f4a90 100644
--- a/common/static/js_test.yml
+++ b/common/static/js_test.yml
@@ -34,6 +34,7 @@ lib_paths:
- js/vendor/underscore-min.js
- js/vendor/backbone-min.js
- js/vendor/jquery.timeago.js
+ - js/vendor/URI.min.js
- coffee/src/ajax_prefix.js
- js/test/add_ajax_prefix.js
- coffee/src/jquery.immediateDescendents.js
diff --git a/lms/djangoapps/django_comment_client/base/views.py b/lms/djangoapps/django_comment_client/base/views.py
index 4e3331e592..4d8bffdd73 100644
--- a/lms/djangoapps/django_comment_client/base/views.py
+++ b/lms/djangoapps/django_comment_client/base/views.py
@@ -50,16 +50,14 @@ def permitted(fn):
return wrapper
-def ajax_content_response(request, course_id, content, template_name):
+def ajax_content_response(request, course_id, content):
context = {
'course_id': course_id,
'content': content,
}
- html = render_to_string(template_name, context)
user_info = cc.User.from_django_user(request.user).to_dict()
annotated_content_info = utils.get_annotated_content_info(course_id, content, request.user, user_info)
return JsonResponse({
- 'html': html,
'content': utils.safe_content(content),
'annotated_content_info': annotated_content_info,
})
@@ -131,7 +129,7 @@ def create_thread(request, course_id, commentable_id):
data = thread.to_dict()
add_courseware_context([data], course)
if request.is_ajax():
- return ajax_content_response(request, course_id, data, 'discussion/ajax_create_thread.html')
+ return ajax_content_response(request, course_id, data)
else:
return JsonResponse(utils.safe_content(data))
@@ -147,7 +145,7 @@ def update_thread(request, course_id, thread_id):
thread.update_attributes(**extract(request.POST, ['body', 'title', 'tags']))
thread.save()
if request.is_ajax():
- return ajax_content_response(request, course_id, thread.to_dict(), 'discussion/ajax_update_thread.html')
+ return ajax_content_response(request, course_id, thread.to_dict())
else:
return JsonResponse(utils.safe_content(thread.to_dict()))
@@ -184,7 +182,7 @@ def _create_comment(request, course_id, thread_id=None, parent_id=None):
user = cc.User.from_django_user(request.user)
user.follow(comment.thread)
if request.is_ajax():
- return ajax_content_response(request, course_id, comment.to_dict(), 'discussion/ajax_create_comment.html')
+ return ajax_content_response(request, course_id, comment.to_dict())
else:
return JsonResponse(utils.safe_content(comment.to_dict()))
@@ -228,7 +226,7 @@ def update_comment(request, course_id, comment_id):
comment.update_attributes(**extract(request.POST, ['body']))
comment.save()
if request.is_ajax():
- return ajax_content_response(request, course_id, comment.to_dict(), 'discussion/ajax_update_comment.html')
+ return ajax_content_response(request, course_id, comment.to_dict())
else:
return JsonResponse(utils.safe_content(comment.to_dict()))
diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py
index 83516fc3ac..4bbcd5acd4 100644
--- a/lms/djangoapps/django_comment_client/forum/views.py
+++ b/lms/djangoapps/django_comment_client/forum/views.py
@@ -248,13 +248,10 @@ def single_thread(request, course_id, discussion_id, thread_id):
with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"):
annotated_content_info = utils.get_annotated_content_infos(course_id, thread, request.user, user_info=user_info)
context = {'thread': thread.to_dict(), 'course_id': course_id}
- # TODO: Remove completely or switch back to server side rendering
- # html = render_to_string('discussion/_ajax_single_thread.html', context)
content = utils.safe_content(thread.to_dict())
with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
add_courseware_context([content], course)
return utils.JsonResponse({
- #'html': html,
'content': content,
'annotated_content_info': annotated_content_info,
})
diff --git a/lms/djangoapps/django_comment_client/helpers.py b/lms/djangoapps/django_comment_client/helpers.py
index 1310c4e0c1..b9f144e76c 100644
--- a/lms/djangoapps/django_comment_client/helpers.py
+++ b/lms/djangoapps/django_comment_client/helpers.py
@@ -31,22 +31,3 @@ def include_mustache_templates():
file_contents = map(read_file, filter(valid_file_name, os.listdir(mustache_dir)))
return '\n'.join(map(wrap_in_tag, map(strip_file_name, file_contents)))
-
-
-def render_content(content, additional_context={}):
-
- context = {
- 'content': extend_content(content),
- content['type']: True,
- }
- if cc_settings.MAX_COMMENT_DEPTH is not None:
- if content['type'] == 'thread':
- if cc_settings.MAX_COMMENT_DEPTH < 0:
- context['max_depth'] = True
- elif content['type'] == 'comment':
- if cc_settings.MAX_COMMENT_DEPTH <= content['depth']:
- context['max_depth'] = True
- context = merge_dict(context, additional_context)
- partial_mustache_helpers = {k: partial(v, content) for k, v in mustache_helpers.items()}
- context = merge_dict(context, partial_mustache_helpers)
- return render_mustache('discussion/mustache/_content.mustache', context)
diff --git a/lms/static/js/URI.min.js b/lms/static/js/URI.min.js
deleted file mode 100644
index 2663ddccd6..0000000000
--- a/lms/static/js/URI.min.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*! URI.js v1.6.3 http://medialize.github.com/URI.js/ */
-(function(){("undefined"!==typeof module&&module.exports?module.exports:window).IPv6={best:function(e){var e=e.toLowerCase().split(":"),h=e.length,j=8;""===e[0]&&""===e[1]&&""===e[2]?(e.shift(),e.shift()):""===e[0]&&""===e[1]?e.shift():""===e[h-1]&&""===e[h-2]&&e.pop();h=e.length;-1!==e[h-1].indexOf(".")&&(j=7);var f;for(f=0;fl;l++)if("0"===h[0]&&1l&&(h=p,l=r)):"0"==e[f]&&(d=!0,p=f,r=1);r>l&&(h=p,l=r);1>>10&1023|55296),a=56320|a&1023);return b+=x(a)}).join("")}function r(g,
-d,e){for(var f=0,g=e?t(g/c):g>>1,g=g+t(g/d);g>B*a>>1;f+=s)g=t(g/B);return t(f+(B+1)*g/(g+b))}function p(b){var c=[],d=b.length,e,f=0,j=C,k=g,m,u,n,o,i;m=b.lastIndexOf(D);0>m&&(m=0);for(u=0;u=d&&h("invalid-input");o=b.charCodeAt(m++);o=10>o-48?o-22:26>o-65?o-65:26>o-97?o-97:s;(o>=s||o>t((v-f)/e))&&h("overflow");f+=o*e;i=n<=k?y:n>=k+a?a:n-k;if(ot(v/o)&&h("overflow");e*=
-o}e=c.length+1;k=r(f-u,e,0==u);t(f/e)>v-j&&h("overflow");j+=t(f/e);f%=e;c.splice(f++,0,j)}return l(c)}function d(b){var c,d,e,j,k,i,m,l,n,o=[],w,p,q,b=f(b);w=b.length;c=C;d=0;k=g;for(i=0;in&&o.push(x(n));for((e=j=o.length)&&o.push(D);e=c&&nt((v-d)/p)&&h("overflow");d+=(m-c)*p;c=m;for(i=0;iv&&h("overflow"),n==c){l=d;for(m=s;;m+=s){n=m<=k?y:m>=k+a?a:m-k;if(l
-n+q%l)-0));l=t(q/l)}o.push(x(l+22+75*(26>l)-0));k=r(d,p,e==j);d=0;++e}++d;++c}return o.join("")}var k,i="function"==typeof define&&"object"==typeof define.amd&&define.amd&&define,q="object"==typeof exports&&exports,z="object"==typeof module&&module,v=2147483647,s=36,y=1,a=26,b=38,c=700,g=72,C=128,D="-",w=/[^ -~]/,F=/^xn--/,E={overflow:"Overflow: input needs wider integers to process.",ucs2decode:"UCS-2(decode): illegal sequence",ucs2encode:"UCS-2(encode): illegal value","not-basic":"Illegal input >= 0x80 (not a basic code point)",
-"invalid-input":"Invalid input"},B=s-y,t=Math.floor,x=String.fromCharCode,A;k={version:"0.3.0",ucs2:{decode:f,encode:l},decode:p,encode:d,toASCII:function(a){return j(a.split("."),function(a){return w.test(a)?"xn--"+d(a):a}).join(".")},toUnicode:function(a){return j(a.split("."),function(a){return F.test(a)?p(a.slice(4).toLowerCase()):a}).join(".")}};if(q)if(z&&z.exports==q)z.exports=k;else for(A in k)k.hasOwnProperty(A)&&(q[A]=k[A]);else i?define("punycode",k):e.punycode=k})(this);
-(function(){var e={list:{ac:"com|gov|mil|net|org",ae:"ac|co|gov|mil|name|net|org|pro|sch",af:"com|edu|gov|net|org",al:"com|edu|gov|mil|net|org",ao:"co|ed|gv|it|og|pb",ar:"com|edu|gob|gov|int|mil|net|org|tur",at:"ac|co|gv|or",au:"asn|com|csiro|edu|gov|id|net|org",ba:"co|com|edu|gov|mil|net|org|rs|unbi|unmo|unsa|untz|unze",bb:"biz|co|com|edu|gov|info|net|org|store|tv",bh:"biz|cc|com|edu|gov|info|net|org",bn:"com|edu|gov|net|org",bo:"com|edu|gob|gov|int|mil|net|org|tv",br:"adm|adv|agr|am|arq|art|ato|b|bio|blog|bmd|cim|cng|cnt|com|coop|ecn|edu|eng|esp|etc|eti|far|flog|fm|fnd|fot|fst|g12|ggf|gov|imb|ind|inf|jor|jus|lel|mat|med|mil|mus|net|nom|not|ntr|odo|org|ppg|pro|psc|psi|qsl|rec|slg|srv|tmp|trd|tur|tv|vet|vlog|wiki|zlg",
-bs:"com|edu|gov|net|org",bz:"du|et|om|ov|rg",ca:"ab|bc|mb|nb|nf|nl|ns|nt|nu|on|pe|qc|sk|yk",ck:"biz|co|edu|gen|gov|info|net|org",cn:"ac|ah|bj|com|cq|edu|fj|gd|gov|gs|gx|gz|ha|hb|he|hi|hl|hn|jl|js|jx|ln|mil|net|nm|nx|org|qh|sc|sd|sh|sn|sx|tj|tw|xj|xz|yn|zj",co:"com|edu|gov|mil|net|nom|org",cr:"ac|c|co|ed|fi|go|or|sa",cy:"ac|biz|com|ekloges|gov|ltd|name|net|org|parliament|press|pro|tm","do":"art|com|edu|gob|gov|mil|net|org|sld|web",dz:"art|asso|com|edu|gov|net|org|pol",ec:"com|edu|fin|gov|info|med|mil|net|org|pro",
-eg:"com|edu|eun|gov|mil|name|net|org|sci",er:"com|edu|gov|ind|mil|net|org|rochest|w",es:"com|edu|gob|nom|org",et:"biz|com|edu|gov|info|name|net|org",fj:"ac|biz|com|info|mil|name|net|org|pro",fk:"ac|co|gov|net|nom|org",fr:"asso|com|f|gouv|nom|prd|presse|tm",gg:"co|net|org",gh:"com|edu|gov|mil|org",gn:"ac|com|gov|net|org",gr:"com|edu|gov|mil|net|org",gt:"com|edu|gob|ind|mil|net|org",gu:"com|edu|gov|net|org",hk:"com|edu|gov|idv|net|org",id:"ac|co|go|mil|net|or|sch|web",il:"ac|co|gov|idf|k12|muni|net|org",
-"in":"ac|co|edu|ernet|firm|gen|gov|i|ind|mil|net|nic|org|res",iq:"com|edu|gov|i|mil|net|org",ir:"ac|co|dnssec|gov|i|id|net|org|sch",it:"edu|gov",je:"co|net|org",jo:"com|edu|gov|mil|name|net|org|sch",jp:"ac|ad|co|ed|go|gr|lg|ne|or",ke:"ac|co|go|info|me|mobi|ne|or|sc",kh:"com|edu|gov|mil|net|org|per",ki:"biz|com|de|edu|gov|info|mob|net|org|tel",km:"asso|com|coop|edu|gouv|k|medecin|mil|nom|notaires|pharmaciens|presse|tm|veterinaire",kn:"edu|gov|net|org",kr:"ac|busan|chungbuk|chungnam|co|daegu|daejeon|es|gangwon|go|gwangju|gyeongbuk|gyeonggi|gyeongnam|hs|incheon|jeju|jeonbuk|jeonnam|k|kg|mil|ms|ne|or|pe|re|sc|seoul|ulsan",
-kw:"com|edu|gov|net|org",ky:"com|edu|gov|net|org",kz:"com|edu|gov|mil|net|org",lb:"com|edu|gov|net|org",lk:"assn|com|edu|gov|grp|hotel|int|ltd|net|ngo|org|sch|soc|web",lr:"com|edu|gov|net|org",lv:"asn|com|conf|edu|gov|id|mil|net|org",ly:"com|edu|gov|id|med|net|org|plc|sch",ma:"ac|co|gov|m|net|org|press",mc:"asso|tm",me:"ac|co|edu|gov|its|net|org|priv",mg:"com|edu|gov|mil|nom|org|prd|tm",mk:"com|edu|gov|inf|name|net|org|pro",ml:"com|edu|gov|net|org|presse",mn:"edu|gov|org",mo:"com|edu|gov|net|org",
-mt:"com|edu|gov|net|org",mv:"aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro",mw:"ac|co|com|coop|edu|gov|int|museum|net|org",mx:"com|edu|gob|net|org",my:"com|edu|gov|mil|name|net|org|sch",nf:"arts|com|firm|info|net|other|per|rec|store|web",ng:"biz|com|edu|gov|mil|mobi|name|net|org|sch",ni:"ac|co|com|edu|gob|mil|net|nom|org",np:"com|edu|gov|mil|net|org",nr:"biz|com|edu|gov|info|net|org",om:"ac|biz|co|com|edu|gov|med|mil|museum|net|org|pro|sch",pe:"com|edu|gob|mil|net|nom|org|sld",ph:"com|edu|gov|i|mil|net|ngo|org",
-pk:"biz|com|edu|fam|gob|gok|gon|gop|gos|gov|net|org|web",pl:"art|bialystok|biz|com|edu|gda|gdansk|gorzow|gov|info|katowice|krakow|lodz|lublin|mil|net|ngo|olsztyn|org|poznan|pwr|radom|slupsk|szczecin|torun|warszawa|waw|wroc|wroclaw|zgora",pr:"ac|biz|com|edu|est|gov|info|isla|name|net|org|pro|prof",ps:"com|edu|gov|net|org|plo|sec",pw:"belau|co|ed|go|ne|or",ro:"arts|com|firm|info|nom|nt|org|rec|store|tm|www",rs:"ac|co|edu|gov|in|org",sb:"com|edu|gov|net|org",sc:"com|edu|gov|net|org",sh:"co|com|edu|gov|net|nom|org",
-sl:"com|edu|gov|net|org",st:"co|com|consulado|edu|embaixada|gov|mil|net|org|principe|saotome|store",sv:"com|edu|gob|org|red",sz:"ac|co|org",tr:"av|bbs|bel|biz|com|dr|edu|gen|gov|info|k12|name|net|org|pol|tel|tsk|tv|web",tt:"aero|biz|cat|co|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel",tw:"club|com|ebiz|edu|game|gov|idv|mil|net|org",mu:"ac|co|com|gov|net|or|org",mz:"ac|co|edu|gov|org",na:"co|com",nz:"ac|co|cri|geek|gen|govt|health|iwi|maori|mil|net|org|parliament|school",
-pa:"abo|ac|com|edu|gob|ing|med|net|nom|org|sld",pt:"com|edu|gov|int|net|nome|org|publ",py:"com|edu|gov|mil|net|org",qa:"com|edu|gov|mil|net|org",re:"asso|com|nom",ru:"ac|adygeya|altai|amur|arkhangelsk|astrakhan|bashkiria|belgorod|bir|bryansk|buryatia|cbg|chel|chelyabinsk|chita|chukotka|chuvashia|com|dagestan|e-burg|edu|gov|grozny|int|irkutsk|ivanovo|izhevsk|jar|joshkar-ola|kalmykia|kaluga|kamchatka|karelia|kazan|kchr|kemerovo|khabarovsk|khakassia|khv|kirov|koenig|komi|kostroma|kranoyarsk|kuban|kurgan|kursk|lipetsk|magadan|mari|mari-el|marine|mil|mordovia|mosreg|msk|murmansk|nalchik|net|nnov|nov|novosibirsk|nsk|omsk|orenburg|org|oryol|penza|perm|pp|pskov|ptz|rnd|ryazan|sakhalin|samara|saratov|simbirsk|smolensk|spb|stavropol|stv|surgut|tambov|tatarstan|tom|tomsk|tsaritsyn|tsk|tula|tuva|tver|tyumen|udm|udmurtia|ulan-ude|vladikavkaz|vladimir|vladivostok|volgograd|vologda|voronezh|vrn|vyatka|yakutia|yamal|yekaterinburg|yuzhno-sakhalinsk",
-rw:"ac|co|com|edu|gouv|gov|int|mil|net",sa:"com|edu|gov|med|net|org|pub|sch",sd:"com|edu|gov|info|med|net|org|tv",se:"a|ac|b|bd|c|d|e|f|g|h|i|k|l|m|n|o|org|p|parti|pp|press|r|s|t|tm|u|w|x|y|z",sg:"com|edu|gov|idn|net|org|per",sn:"art|com|edu|gouv|org|perso|univ",sy:"com|edu|gov|mil|net|news|org",th:"ac|co|go|in|mi|net|or",tj:"ac|biz|co|com|edu|go|gov|info|int|mil|name|net|nic|org|test|web",tn:"agrinet|com|defense|edunet|ens|fin|gov|ind|info|intl|mincom|nat|net|org|perso|rnrt|rns|rnu|tourism",tz:"ac|co|go|ne|or",
-ua:"biz|cherkassy|chernigov|chernovtsy|ck|cn|co|com|crimea|cv|dn|dnepropetrovsk|donetsk|dp|edu|gov|if|in|ivano-frankivsk|kh|kharkov|kherson|khmelnitskiy|kiev|kirovograd|km|kr|ks|kv|lg|lugansk|lutsk|lviv|me|mk|net|nikolaev|od|odessa|org|pl|poltava|pp|rovno|rv|sebastopol|sumy|te|ternopil|uzhgorod|vinnica|vn|zaporizhzhe|zhitomir|zp|zt",ug:"ac|co|go|ne|or|org|sc",uk:"ac|bl|british-library|co|cym|gov|govt|icnet|jet|lea|ltd|me|mil|mod|national-library-scotland|nel|net|nhs|nic|nls|org|orgn|parliament|plc|police|sch|scot|soc",
-us:"dni|fed|isa|kids|nsn",uy:"com|edu|gub|mil|net|org",ve:"co|com|edu|gob|info|mil|net|org|web",vi:"co|com|k12|net|org",vn:"ac|biz|com|edu|gov|health|info|int|name|net|org|pro",ye:"co|com|gov|ltd|me|net|org|plc",yu:"ac|co|edu|gov|org",za:"ac|agric|alt|bourse|city|co|cybernet|db|edu|gov|grondar|iaccess|imt|inca|landesign|law|mil|net|ngo|nis|nom|olivetti|org|pix|school|tm|web",zm:"ac|co|com|edu|gov|net|org|sch"},has_expression:null,is_expression:null,has:function(h){return!!h.match(e.has_expression)},
-is:function(h){return!!h.match(e.is_expression)},get:function(h){return(h=h.match(e.has_expression))&&h[1]||null},init:function(){var h="",j;for(j in e.list)Object.prototype.hasOwnProperty.call(e.list,j)&&(h+="|("+("("+e.list[j]+")."+j)+")");e.has_expression=RegExp(".("+h.substr(1)+")$","i");e.is_expression=RegExp("^("+h.substr(1)+")$","i")}};e.init();"undefined"!==typeof module&&module.exports?module.exports=e:window.SecondLevelDomains=e})();
-(function(e){function h(a){return a.replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")}function j(a){return"[object Array]"===""+Object.prototype.toString.call(a)}var f="undefined"!==typeof module&&module.exports,l=f?require("./punycode"):window.punycode,r=f?require("./IPv6"):window.IPv6,p=f?require("./SecondLevelDomains"):window.SecondLevelDomains,d=function(a,b){if(!(this instanceof d))return new d(a);a===e&&(a=location.href+"");this.href(a);return b!==e?this.absoluteTo(b):this},f=d.prototype;d.idn_expression=
-/[^a-z0-9\.-]/i;d.punycode_expression=/(xn--)/i;d.ip4_expression=/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;d.ip6_expression=/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
-d.find_uri_expression=/\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u2018\u2019]))/ig;d.defaultPorts={http:"80",https:"443",ftp:"21"};d.invalid_hostname_characters=/[^a-zA-Z0-9\.-]/;d.encode=encodeURIComponent;d.decode=decodeURIComponent;d.iso8859=function(){d.encode=escape;d.decode=unescape};d.unicode=function(){d.encode=encodeURIComponent;
-d.decode=decodeURIComponent};d.characters={pathname:{encode:{expression:/%(24|26|2B|2C|3B|3D|3A|40)/ig,map:{"%24":"$","%26":"&","%2B":"+","%2C":",","%3B":";","%3D":"=","%3A":":","%40":"@"}},decode:{expression:/[\/\?#]/g,map:{"/":"%2F","?":"%3F","#":"%23"}}}};d.encodeQuery=function(a){return d.encode(a+"").replace(/%20/g,"+")};d.decodeQuery=function(a){return d.decode((a+"").replace(/\+/g,"%20"))};d.recodePath=function(a){for(var a=(a+"").split("/"),b=0,c=a.length;bd)return a[0]===b[0]&&"/"===a[0]?"/":"";"/"!==a[d]&&(d=a.substring(0,d).lastIndexOf("/"));
-return a.substring(0,d+1)};d.withinString=function(a,b){return a.replace(d.find_uri_expression,b)};d.ensureValidHostname=function(a){if(a.match(d.invalid_hostname_characters)){if(!l)throw new TypeError("Hostname '"+a+"' contains characters other than [A-Z0-9.-] and Punycode.js is not available");if(l.toASCII(a).match(d.invalid_hostname_characters))throw new TypeError("Hostname '"+a+"' contains characters other than [A-Z0-9.-]");}};f.build=function(a){if(!0===a)this._deferred_build=!0;else if(a===
-e||this._deferred_build)this._string=d.build(this._parts),this._deferred_build=!1;return this};f.clone=function(){return new d(this)};f.toString=function(){return this.build(!1)._string};f.valueOf=function(){return this.toString()};k={protocol:"protocol",username:"username",password:"password",hostname:"hostname",port:"port"};q=function(a){return function(b,c){if(b===e)return this._parts[a]||"";this._parts[a]=b;this.build(!c);return this}};for(i in k)f[i]=q(k[i]);k={query:"?",fragment:"#"};q=function(a,
-b){return function(c,d){if(c===e)return this._parts[a]||"";null!==c&&(c+="",c[0]===b&&(c=c.substring(1)));this._parts[a]=c;this.build(!d);return this}};for(i in k)f[i]=q(i,k[i]);k={search:["?","query"],hash:["#","fragment"]};q=function(a,b){return function(c,d){var e=this[a](c,d);return"string"===typeof e&&e.length?b+e:e}};for(i in k)f[i]=q(k[i][1],k[i][0]);f.pathname=function(a,b){if(a===e||!0===a){var c=this._parts.path||(this._parts.urn?"":"/");return a?d.decodePath(c):c}this._parts.path=a?d.recodePath(a):
-"/";this.build(!b);return this};f.path=f.pathname;f.href=function(a,b){if(a===e)return this.toString();this._string="";this._parts={protocol:null,username:null,password:null,hostname:null,urn:null,port:null,path:null,query:null,fragment:null};var c=a instanceof d,g="object"===typeof a&&(a.hostname||a.path),f;if("string"===typeof a)this._parts=d.parse(a);else if(c||g)for(f in c=c?a._parts:a,c)Object.hasOwnProperty.call(this._parts,f)&&(this._parts[f]=c[f]);else throw new TypeError("invalid input");
-this.build(!b);return this};f.is=function(a){var b=!1,c=!1,g=!1,e=!1,f=!1,h=!1,i=!1,j=!this._parts.urn;this._parts.hostname&&(j=!1,c=d.ip4_expression.test(this._parts.hostname),g=d.ip6_expression.test(this._parts.hostname),b=c||g,f=(e=!b)&&p&&p.has(this._parts.hostname),h=e&&d.idn_expression.test(this._parts.hostname),i=e&&d.punycode_expression.test(this._parts.hostname));switch(a.toLowerCase()){case "relative":return j;case "absolute":return!j;case "domain":case "name":return e;case "sld":return f;
-case "ip":return b;case "ip4":case "ipv4":case "inet4":return c;case "ip6":case "ipv6":case "inet6":return g;case "idn":return h;case "url":return!this._parts.urn;case "urn":return!!this._parts.urn;case "punycode":return i}return null};var z=f.protocol,v=f.port,s=f.hostname;f.protocol=function(a,b){if(a!==e&&a&&(a=a.replace(/:(\/\/)?$/,""),a.match(/[^a-zA-z0-9\.+-]/)))throw new TypeError("Protocol '"+a+"' contains characters other than [A-Z0-9.+-]");return z.call(this,a,b)};f.scheme=f.protocol;f.port=
-function(a,b){if(this._parts.urn)return a===e?"":this;if(a!==e&&(0===a&&(a=null),a&&(a+="",":"===a[0]&&(a=a.substring(1)),a.match(/[^0-9]/))))throw new TypeError("Port '"+a+"' contains characters other than [0-9]");return v.call(this,a,b)};f.hostname=function(a,b){if(this._parts.urn)return a===e?"":this;if(a!==e){var c={};d.parseHost(a,c);a=c.hostname}return s.call(this,a,b)};f.host=function(a,b){if(this._parts.urn)return a===e?"":this;if(a===e)return this._parts.hostname?d.buildHost(this._parts):
-"";d.parseHost(a,this._parts);this.build(!b);return this};f.authority=function(a,b){if(this._parts.urn)return a===e?"":this;if(a===e)return this._parts.hostname?d.buildAuthority(this._parts):"";d.parseAuthority(a,this._parts);this.build(!b);return this};f.userinfo=function(a,b){if(this._parts.urn)return a===e?"":this;if(a===e){if(!this._parts.username)return"";var c=d.buildUserinfo(this._parts);return c.substring(0,c.length-1)}"@"!==a[a.length-1]&&(a+="@");d.parseUserinfo(a,this._parts);this.build(!b);
-return this};f.subdomain=function(a,b){if(this._parts.urn)return a===e?"":this;if(a===e)return!this._parts.hostname||this.is("IP")?"":this._parts.hostname.substring(0,this._parts.hostname.length-this.domain().length-1)||"";var c=this._parts.hostname.substring(0,this._parts.hostname.length-this.domain().length),c=RegExp("^"+h(c));a&&"."!==a[a.length-1]&&(a+=".");a&&d.ensureValidHostname(a);this._parts.hostname=this._parts.hostname.replace(c,a);this.build(!b);return this};f.domain=function(a,b){if(this._parts.urn)return a===
-e?"":this;"boolean"==typeof a&&(b=a,a=e);if(a===e){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.match(/\./g);if(c&&2>c.length)return this._parts.hostname;c=this._parts.hostname.length-this.tld(b).length-1;c=this._parts.hostname.lastIndexOf(".",c-1)+1;return this._parts.hostname.substring(c)||""}if(!a)throw new TypeError("cannot set domain empty");d.ensureValidHostname(a);this._parts.hostname=!this._parts.hostname||this.is("IP")?a:this._parts.hostname.replace(RegExp(h(this.domain())+
-"$"),a);this.build(!b);return this};f.tld=function(a,b){if(this._parts.urn)return a===e?"":this;"boolean"==typeof a&&(b=a,a=e);if(a===e){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.substring(this._parts.hostname.lastIndexOf(".")+1);return!0!==b&&p&&p.list[c.toLowerCase()]?p.get(this._parts.hostname)||c:c}if(a)if(a.match(/[^a-zA-Z0-9-]/))if(p&&p.is(a))c=RegExp(h(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(c,a);else throw new TypeError("TLD '"+
-a+"' contains characters other than [A-Z0-9]");else{if(!this._parts.hostname||this.is("IP"))throw new ReferenceError("cannot set TLD on non-domain host");c=RegExp(h(this.tld())+"$");this._parts.hostname=this._parts.hostname.replace(c,a)}else throw new TypeError("cannot set TLD empty");this.build(!b);return this};f.directory=function(a,b){if(this._parts.urn)return a===e?"":this;if(a===e||!0===a){if(!this._parts.path&&!this._parts.hostname)return"";if("/"===this._parts.path)return"/";var c=this._parts.path.substring(0,
-this._parts.path.length-this.filename().length-1)||(this._parts.hostname?"/":"");return a?d.decodePath(c):c}c=this._parts.path.substring(0,this._parts.path.length-this.filename().length);c=RegExp("^"+h(c));this.is("relative")||(a||(a="/"),"/"!==a[0]&&(a="/"+a));a&&"/"!==a[a.length-1]&&(a+="/");a=d.recodePath(a);this._parts.path=this._parts.path.replace(c,a);this.build(!b);return this};f.filename=function(a,b){if(this._parts.urn)return a===e?"":this;if(a===e||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";
-var c=this._parts.path.substring(this._parts.path.lastIndexOf("/")+1);return a?d.decodePathSegment(c):c}c=!1;"/"===a[0]&&(a=a.substring(1));a.match(/\.?\//)&&(c=!0);var g=RegExp(h(this.filename())+"$"),a=d.recodePath(a);this._parts.path=this._parts.path.replace(g,a);c?this.normalizePath(b):this.build(!b);return this};f.suffix=function(a,b){if(this._parts.urn)return a===e?"":this;if(a===e||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var c=this.filename(),g=c.lastIndexOf(".");if(-1===
-g)return"";c=c.substring(g+1);c=/^[a-z0-9%]+$/i.test(c)?c:"";return a?d.decodePathSegment(c):c}"."===a[0]&&(a=a.substring(1));if(c=this.suffix())g=a?RegExp(h(c)+"$"):RegExp(h("."+c)+"$");else{if(!a)return this;this._parts.path+="."+d.recodePath(a)}g&&(a=d.recodePath(a),this._parts.path=this._parts.path.replace(g,a));this.build(!b);return this};var y=f.query;f.query=function(a,b){return!0===a?d.parseQuery(this._parts.query):a!==e&&"string"!==typeof a?(this._parts.query=d.buildQuery(a),this.build(!b),
-this):y.call(this,a,b)};f.addQuery=function(a,b,c){var g=d.parseQuery(this._parts.query);d.addQuery(g,a,b);this._parts.query=d.buildQuery(g);"string"!==typeof a&&(c=b);this.build(!c);return this};f.removeQuery=function(a,b,c){var g=d.parseQuery(this._parts.query);d.removeQuery(g,a,b);this._parts.query=d.buildQuery(g);"string"!==typeof a&&(c=b);this.build(!c);return this};f.addSearch=f.addQuery;f.removeSearch=f.removeQuery;f.normalize=function(){return this._parts.urn?this.normalizeProtocol(!1).normalizeQuery(!1).normalizeFragment(!1).build():
-this.normalizeProtocol(!1).normalizeHostname(!1).normalizePort(!1).normalizePath(!1).normalizeQuery(!1).normalizeFragment(!1).build()};f.normalizeProtocol=function(a){"string"===typeof this._parts.protocol&&(this._parts.protocol=this._parts.protocol.toLowerCase(),this.build(!a));return this};f.normalizeHostname=function(a){this._parts.hostname&&(this.is("IDN")&&l?this._parts.hostname=l.toASCII(this._parts.hostname):this.is("IPv6")&&r&&(this._parts.hostname=r.best(this._parts.hostname)),this._parts.hostname=
-this._parts.hostname.toLowerCase(),this.build(!a));return this};f.normalizePort=function(a){"string"===typeof this._parts.protocol&&this._parts.port===d.defaultPorts[this._parts.protocol]&&(this._parts.port=null,this.build(!a));return this};f.normalizePath=function(a){if(this._parts.urn||!this._parts.path||"/"===this._parts.path)return this;var b,c,g=this._parts.path,e,f;"/"!==g[0]&&("."===g[0]&&(c=g.substring(0,g.indexOf("/"))),b=!0,g="/"+g);for(g=g.replace(/(\/(\.\/)+)|\/{2,}/g,"/");;){e=g.indexOf("/../");
-if(-1===e)break;else if(0===e){g=g.substring(3);break}f=g.substring(0,e).lastIndexOf("/");-1===f&&(f=e);g=g.substring(0,f)+g.substring(e+3)}b&&this.is("relative")&&(g=c?c+g:g.substring(1));g=d.recodePath(g);this._parts.path=g;this.build(!a);return this};f.normalizePathname=f.normalizePath;f.normalizeQuery=function(a){"string"===typeof this._parts.query&&(this._parts.query.length?this.query(d.parseQuery(this._parts.query)):this._parts.query=null,this.build(!a));return this};f.normalizeFragment=function(a){this._parts.fragment||
-(this._parts.fragment=null,this.build(!a));return this};f.normalizeSearch=f.normalizeQuery;f.normalizeHash=f.normalizeFragment;f.iso8859=function(){var a=d.encode,b=d.decode;d.encode=escape;d.decode=decodeURIComponent;this.normalize();d.encode=a;d.decode=b;return this};f.unicode=function(){var a=d.encode,b=d.decode;d.encode=encodeURIComponent;d.decode=unescape;this.normalize();d.encode=a;d.decode=b;return this};f.readable=function(){var a=this.clone();a.username("").password("").normalize();var b=
-"";a._parts.protocol&&(b+=a._parts.protocol+"://");a._parts.hostname&&(a.is("punycode")&&l?(b+=l.toUnicode(a._parts.hostname),a._parts.port&&(b+=":"+a._parts.port)):b+=a.host());a._parts.hostname&&(a._parts.path&&"/"!==a._parts.path[0])&&(b+="/");b+=a.path(!0);if(a._parts.query){for(var c="",g=0,f=a._parts.query.split("&"),h=f.length;g
-${renderer.render_comments(thread.get('children'))}
diff --git a/lms/templates/discussion/_content_renderer.html b/lms/templates/discussion/_content_renderer.html
deleted file mode 100644
index 1de687cb19..0000000000
--- a/lms/templates/discussion/_content_renderer.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<%! import django_comment_client.helpers as helpers %>
-
-<%def name="render_content(content, *args, **kwargs)">
- ${helpers.render_content(content, *args, **kwargs)}
-%def>
-
-<%def name="render_content_with_comments(content, *args, **kwargs)">
-
- ${render_content(content, *args, **kwargs)}
- ${render_comments(content.get('children', []), *args, **kwargs)}
-
-%def>
-
-<%def name="render_comments(comments, *args, **kwargs)">
-
-%def>
diff --git a/lms/templates/discussion/_forum.html b/lms/templates/discussion/_forum.html
deleted file mode 100644
index b43efae666..0000000000
--- a/lms/templates/discussion/_forum.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<%namespace name="renderer" file="_content_renderer.html"/>
-
-
-
-
-
- <%include file="_search_bar.html" />
-
-
- % if len(threads) == 0:
-
- <%include file="_blank_slate.html" />
-
-
- % else:
- <%include file="_sort.html" />
-
- % for thread in threads:
- ${renderer.render_content_with_comments(thread)}
- % endfor
-
- <%include file="_paginator.html" />
- % endif
-
-
-<%include file="_js_data.html" />
diff --git a/lms/templates/discussion/_inline.html b/lms/templates/discussion/_inline.html
deleted file mode 100644
index abef7f39e8..0000000000
--- a/lms/templates/discussion/_inline.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<%namespace name="renderer" file="_content_renderer.html"/>
-
-
-
-
-
-
- % for thread in threads:
- ${renderer.render_content_with_comments(thread)}
- % endfor
-
-
- <%include file="_paginator.html" />
-
-
-<%include file="_js_data.html" />
diff --git a/lms/templates/discussion/_js_dependencies.html b/lms/templates/discussion/_js_dependencies.html
index bde873fee1..961f254efe 100644
--- a/lms/templates/discussion/_js_dependencies.html
+++ b/lms/templates/discussion/_js_dependencies.html
@@ -9,7 +9,7 @@
-
+
diff --git a/lms/templates/discussion/_js_head_dependencies.html b/lms/templates/discussion/_js_head_dependencies.html
index bde873fee1..961f254efe 100644
--- a/lms/templates/discussion/_js_head_dependencies.html
+++ b/lms/templates/discussion/_js_head_dependencies.html
@@ -9,7 +9,7 @@
-
+
diff --git a/lms/templates/discussion/_single_thread.html b/lms/templates/discussion/_single_thread.html
deleted file mode 100644
index a14e03d10f..0000000000
--- a/lms/templates/discussion/_single_thread.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<%! from django.utils.translation import ugettext as _ %>
-<%namespace name="renderer" file="_content_renderer.html"/>
-<%! from django_comment_client.mustache_helpers import url_for_user %>
-
-
-
-
-
-
-
- ${thread['body']}
-
-
-
- % for reply in thread.get("children", []):
- -
-
${reply['body']}
-
-
- % endfor
-
-
-
-<%include file="_js_data.html" />
diff --git a/lms/templates/discussion/_underscore_templates.html b/lms/templates/discussion/_underscore_templates.html
index e331a779a5..2ebd1465e1 100644
--- a/lms/templates/discussion/_underscore_templates.html
+++ b/lms/templates/discussion/_underscore_templates.html
@@ -31,8 +31,8 @@
${"<%- obj.group_string%>"}
${"<% } %>"}
-
- + ${'<%- votes["up_count"] %>'}votes (click to vote)
+
+ ${'<%- votes["up_count"] %>'} votes (click to vote)
${'<%- title %>'}
${"<% if (obj.username) { %>"}
@@ -123,7 +123,7 @@