Commit 2b8c02a7 by E. Kolpakov Committed by Andy Armstrong

Discussion coffee files to JS

parent b1af59ad
!view/discussion_thread_edit_view_spec.js
!view/discussion_topic_menu_view_spec.js
!views/discussion_thread_edit_view.js
!views/discussion_topic_menu_view.js
if Backbone?
class @DiscussionRouter extends Backbone.Router
routes:
"": "allThreads"
":forum_name/threads/:thread_id" : "showThread"
initialize: (options) ->
@discussion = options['discussion']
@course_settings = options['course_settings']
@nav = new DiscussionThreadListView(
collection: @discussion,
el: $(".forum-nav"),
courseSettings: @course_settings
)
@nav.on "thread:selected", @navigateToThread
@nav.on "thread:removed", @navigateToAllThreads
@nav.on "threads:rendered", @setActiveThread
@nav.on "thread:created", @navigateToThread
@nav.render()
@newPost = $('.new-post-article')
@newPostView = new NewPostView(
el: @newPost,
collection: @discussion,
course_settings: @course_settings,
mode: "tab"
)
@newPostView.render()
@listenTo( @newPostView, 'newPost:cancel', @hideNewPost )
$('.new-post-btn').bind "click", @showNewPost
$('.new-post-btn').bind "keydown", (event) => DiscussionUtil.activateOnSpace(event, @showNewPost)
allThreads: ->
@nav.updateSidebar()
@nav.goHome()
setActiveThread: =>
if @thread
@nav.setActiveThread(@thread.get("id"))
else
@nav.goHome
showThread: (forum_name, thread_id) ->
@thread = @discussion.get(thread_id)
@thread.set("unread_comments_count", 0)
@thread.set("read", true)
@setActiveThread()
@showMain()
showMain: =>
if(@main)
@main.cleanup()
@main.undelegateEvents()
unless($(".forum-content").is(":visible"))
$(".forum-content").fadeIn()
if(@newPost.is(":visible"))
@newPost.fadeOut()
@main = new DiscussionThreadView(
el: $(".forum-content"),
model: @thread,
mode: "tab",
course_settings: @course_settings,
)
@main.render()
@main.on "thread:responses:rendered", =>
@nav.updateSidebar()
@thread.on "thread:thread_type_updated", @showMain
navigateToThread: (thread_id) =>
thread = @discussion.get(thread_id)
@navigate("#{thread.get("commentable_id")}/threads/#{thread_id}", trigger: true)
navigateToAllThreads: =>
@navigate("", trigger: true)
showNewPost: (event) =>
$('.forum-content').fadeOut(
duration: 200
complete: =>
@newPost.fadeIn(200).focus()
)
hideNewPost: =>
@newPost.fadeOut(
duration: 200
complete: =>
$('.forum-content').fadeIn(200).find('.thread-wrapper').focus()
)
/* globals DiscussionThreadListView, DiscussionThreadView, DiscussionUtil, NewPostView */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.DiscussionRouter = (function(_super) {
__extends(DiscussionRouter, _super);
function DiscussionRouter() {
var self = this;
this.hideNewPost = function() {
return DiscussionRouter.prototype.hideNewPost.apply(self, arguments);
};
this.showNewPost = function() {
return DiscussionRouter.prototype.showNewPost.apply(self, arguments);
};
this.navigateToAllThreads = function() {
return DiscussionRouter.prototype.navigateToAllThreads.apply(self, arguments);
};
this.navigateToThread = function() {
return DiscussionRouter.prototype.navigateToThread.apply(self, arguments);
};
this.showMain = function() {
return DiscussionRouter.prototype.showMain.apply(self, arguments);
};
this.setActiveThread = function() {
return DiscussionRouter.prototype.setActiveThread.apply(self, arguments);
};
return DiscussionRouter.__super__.constructor.apply(this, arguments);
}
DiscussionRouter.prototype.routes = {
"": "allThreads",
":forum_name/threads/:thread_id": "showThread"
};
DiscussionRouter.prototype.initialize = function(options) {
var self = this;
this.discussion = options.discussion;
this.course_settings = options.course_settings;
this.nav = new DiscussionThreadListView({
collection: this.discussion,
el: $(".forum-nav"),
courseSettings: this.course_settings
});
this.nav.on("thread:selected", this.navigateToThread);
this.nav.on("thread:removed", this.navigateToAllThreads);
this.nav.on("threads:rendered", this.setActiveThread);
this.nav.on("thread:created", this.navigateToThread);
this.nav.render();
this.newPost = $('.new-post-article');
this.newPostView = new NewPostView({
el: this.newPost,
collection: this.discussion,
course_settings: this.course_settings,
mode: "tab"
});
this.newPostView.render();
this.listenTo(this.newPostView, 'newPost:cancel', this.hideNewPost);
$('.new-post-btn').bind("click", this.showNewPost);
return $('.new-post-btn').bind("keydown", function(event) {
return DiscussionUtil.activateOnSpace(event, self.showNewPost);
});
};
DiscussionRouter.prototype.allThreads = function() {
this.nav.updateSidebar();
return this.nav.goHome();
};
DiscussionRouter.prototype.setActiveThread = function() {
if (this.thread) {
return this.nav.setActiveThread(this.thread.get("id"));
} else {
return this.nav.goHome;
}
};
DiscussionRouter.prototype.showThread = function(forum_name, thread_id) {
this.thread = this.discussion.get(thread_id);
this.thread.set("unread_comments_count", 0);
this.thread.set("read", true);
this.setActiveThread();
return this.showMain();
};
DiscussionRouter.prototype.showMain = function() {
var self = this;
if (this.main) {
this.main.cleanup();
this.main.undelegateEvents();
}
if (!($(".forum-content").is(":visible"))) {
$(".forum-content").fadeIn();
}
if (this.newPost.is(":visible")) {
this.newPost.fadeOut();
}
this.main = new DiscussionThreadView({
el: $(".forum-content"),
model: this.thread,
mode: "tab",
course_settings: this.course_settings
});
this.main.render();
this.main.on("thread:responses:rendered", function() {
return self.nav.updateSidebar();
});
return this.thread.on("thread:thread_type_updated", this.showMain);
};
DiscussionRouter.prototype.navigateToThread = function(thread_id) {
var thread;
thread = this.discussion.get(thread_id);
return this.navigate("" + (thread.get("commentable_id")) + "/threads/" + thread_id, {
trigger: true
});
};
DiscussionRouter.prototype.navigateToAllThreads = function() {
return this.navigate("", {
trigger: true
});
};
DiscussionRouter.prototype.showNewPost = function() {
var self = this;
return $('.forum-content').fadeOut({
duration: 200,
complete: function() {
return self.newPost.fadeIn(200).focus();
}
});
};
DiscussionRouter.prototype.hideNewPost = function() {
return this.newPost.fadeOut({
duration: 200,
complete: function() {
return $('.forum-content').fadeIn(200).find('.thread-wrapper').focus();
}
});
};
return DiscussionRouter;
})(Backbone.Router);
}
}).call(window);
if Backbone?
DiscussionApp =
start: (elem)->
# TODO: Perhaps eliminate usage of global variables when possible
DiscussionUtil.loadRolesFromContainer()
element = $(elem)
window.$$course_id = element.data("course-id")
window.courseName = element.data("course-name")
user_info = element.data("user-info")
sort_preference = element.data("sort-preference")
threads = element.data("threads")
thread_pages = element.data("thread-pages")
content_info = element.data("content-info")
user = new DiscussionUser(user_info)
DiscussionUtil.setUser(user)
window.user = user
Content.loadContentInfos(content_info)
discussion = new Discussion(threads, {pages: thread_pages, sort: sort_preference})
course_settings = new DiscussionCourseSettings(element.data("course-settings"))
new DiscussionRouter({discussion: discussion, course_settings: course_settings})
Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"})
DiscussionProfileApp =
start: (elem) ->
# Roles are not included in user profile page, but they are not used for anything
DiscussionUtil.loadRoles({"Moderator": [], "Administrator": [], "Community TA": []})
element = $(elem)
window.$$course_id = element.data("course-id")
threads = element.data("threads")
user_info = element.data("user-info")
window.user = new DiscussionUser(user_info)
page = element.data("page")
numPages = element.data("num-pages")
new DiscussionUserProfileView(el: element, collection: threads, page: page, numPages: numPages)
$ ->
$("section.discussion").each (index, elem) ->
DiscussionApp.start(elem)
$("section.discussion-user-threads").each (index, elem) ->
DiscussionProfileApp.start(elem)
/* global $$course_id, Content, Discussion, DiscussionRouter, DiscussionCourseSettings,
DiscussionUser, DiscussionUserProfileView, DiscussionUtil */
(function() {
'use strict';
var DiscussionApp, DiscussionProfileApp;
if (typeof Backbone !== "undefined" && Backbone !== null) {
DiscussionApp = {
start: function(elem) {
var content_info, course_settings, discussion, element, sort_preference, thread_pages, threads,
user, user_info;
DiscussionUtil.loadRolesFromContainer();
element = $(elem);
window.$$course_id = element.data("course-id");
window.courseName = element.data("course-name");
user_info = element.data("user-info");
sort_preference = element.data("sort-preference");
threads = element.data("threads");
thread_pages = element.data("thread-pages");
content_info = element.data("content-info");
user = new DiscussionUser(user_info);
DiscussionUtil.setUser(user);
window.user = user;
Content.loadContentInfos(content_info);
discussion = new Discussion(threads, {
pages: thread_pages,
sort: sort_preference
});
course_settings = new DiscussionCourseSettings(element.data("course-settings"));
// suppressing Do not use 'new' for side effects.
/* jshint -W031*/
new DiscussionRouter({
discussion: discussion,
course_settings: course_settings
});
/* jshint +W031*/
return Backbone.history.start({
pushState: true,
root: "/courses/" + $$course_id + "/discussion/forum/"
});
}
};
DiscussionProfileApp = {
start: function(elem) {
var element, numPages, page, threads, user_info;
DiscussionUtil.loadRoles({
"Moderator": [],
"Administrator": [],
"Community TA": []
});
element = $(elem);
window.$$course_id = element.data("course-id");
threads = element.data("threads");
user_info = element.data("user-info");
window.user = new DiscussionUser(user_info);
page = element.data("page");
numPages = element.data("num-pages");
return new DiscussionUserProfileView({
el: element,
collection: threads,
page: page,
numPages: numPages
});
}
};
$(function() {
$("section.discussion").each(function(index, elem) {
return DiscussionApp.start(elem);
});
return $("section.discussion-user-threads").each(function(index, elem) {
return DiscussionProfileApp.start(elem);
});
});
}
}).call(window);
if Backbone?
class @DiscussionCourseSettings extends Backbone.Model
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.DiscussionCourseSettings = (function(_super) {
__extends(DiscussionCourseSettings, _super);
function DiscussionCourseSettings() {
return DiscussionCourseSettings.__super__.constructor.apply(this, arguments);
}
return DiscussionCourseSettings;
})(Backbone.Model);
}
}).call(this);
if Backbone?
class @DiscussionUser extends Backbone.Model
following: (thread) ->
_.include(@get('subscribed_thread_ids'), thread.id)
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
voted: (thread) ->
_.include(@get('upvoted_ids'), thread.id)
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
vote: (thread) ->
@get('upvoted_ids').push(thread.id)
thread.vote()
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.DiscussionUser = (function(_super) {
unvote: (thread) ->
@set('upvoted_ids', _.without(@get('upvoted_ids'), thread.id))
thread.unvote()
__extends(DiscussionUser, _super);
function DiscussionUser() {
return DiscussionUser.__super__.constructor.apply(this, arguments);
}
DiscussionUser.prototype.following = function(thread) {
return _.include(this.get('subscribed_thread_ids'), thread.id);
};
DiscussionUser.prototype.voted = function(thread) {
return _.include(this.get('upvoted_ids'), thread.id);
};
DiscussionUser.prototype.vote = function(thread) {
this.get('upvoted_ids').push(thread.id);
return thread.vote();
};
DiscussionUser.prototype.unvote = function(thread) {
this.set('upvoted_ids', _.without(this.get('upvoted_ids'), thread.id));
return thread.unvote();
};
return DiscussionUser;
})(Backbone.Model);
}
}).call(this);
(function(Backbone) {
/* globals DiscussionTopicMenuView, DiscussionUtil */
(function() {
'use strict';
if (Backbone) {
this.DiscussionThreadEditView = Backbone.View.extend({
......@@ -50,7 +51,7 @@
return this;
},
isTabMode: function () {
isTabMode: function() {
return this.mode === 'tab';
},
......@@ -85,7 +86,7 @@
this.model.set(postData).unset('abbreviatedBody');
this.trigger('thread:updated');
if (this.threadType !== threadType) {
this.model.set("thread_type", threadType)
this.model.set("thread_type", threadType);
this.model.trigger('thread:thread_type_updated');
this.trigger('comment:endorse');
}
......@@ -109,4 +110,4 @@
}
});
}
}).call(this, Backbone);
}).call(window); // jshint ignore:line
if Backbone?
class @DiscussionThreadProfileView extends Backbone.View
render: ->
@convertMath()
@abbreviateBody()
params = $.extend(@model.toJSON(),{permalink: @model.urlFor('retrieve')})
if not @model.get('anonymous')
params = $.extend(params, user:{username: @model.username, user_url: @model.user_url})
@$el.html(_.template($("#profile-thread-template").html())(params))
@$("span.timeago").timeago()
element = @$(".post-body")
if MathJax?
MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]]
@
convertMath: ->
@model.set('markdownBody', DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight @model.get('body'))
abbreviateBody: ->
abbreviated = DiscussionUtil.abbreviateHTML @model.get('markdownBody'), 140
@model.set('abbreviatedBody', abbreviated)
/* globals DiscussionUtil, MathJax */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.DiscussionThreadProfileView = (function(_super) {
__extends(DiscussionThreadProfileView, _super);
function DiscussionThreadProfileView() {
return DiscussionThreadProfileView.__super__.constructor.apply(this, arguments);
}
DiscussionThreadProfileView.prototype.render = function() {
var element, params;
this.convertMath();
this.abbreviateBody();
params = $.extend(this.model.toJSON(), {
permalink: this.model.urlFor('retrieve')
});
if (!this.model.get('anonymous')) {
params = $.extend(params, {
user: {
username: this.model.username,
user_url: this.model.user_url
}
});
}
this.$el.html(_.template($("#profile-thread-template").html())(params));
this.$("span.timeago").timeago();
element = this.$(".post-body");
if (typeof MathJax !== "undefined" && MathJax !== null) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, element[0]]);
}
return this;
};
DiscussionThreadProfileView.prototype.convertMath = function() {
return this.model.set(
'markdownBody',
DiscussionUtil.postMathJaxProcessor(DiscussionUtil.markdownWithHighlight(this.model.get('body')))
);
};
DiscussionThreadProfileView.prototype.abbreviateBody = function() {
var abbreviated;
abbreviated = DiscussionUtil.abbreviateHTML(this.model.get('markdownBody'), 140);
return this.model.set('abbreviatedBody', abbreviated);
};
return DiscussionThreadProfileView;
})(Backbone.View);
}
}).call(window);
if Backbone?
class @DiscussionThreadShowView extends DiscussionContentShowView
initialize: (options) ->
super()
@mode = options.mode or "inline" # allowed values are "tab" or "inline"
if @mode not in ["tab", "inline"]
throw new Error("invalid mode: " + @mode)
renderTemplate: ->
@template = _.template($("#thread-show-template").html())
context = $.extend(
{
mode: @mode,
flagged: @model.isFlagged(),
author_display: @getAuthorDisplay(),
cid: @model.cid,
readOnly: $('.discussion-module').data('read-only')
},
@model.attributes,
)
@template(context)
render: ->
@$el.html(@renderTemplate())
@delegateEvents()
@renderAttrs()
@$("span.timeago").timeago()
@convertMath()
@highlight @$(".post-body")
@highlight @$("h1,h3")
@
convertMath: ->
element = @$(".post-body")
element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.text()
if MathJax?
MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]]
edit: (event) ->
@trigger "thread:edit", event
_delete: (event) ->
@trigger "thread:_delete", event
highlight: (el) ->
if el.html()
el.html(el.html().replace(/&lt;mark&gt;/g, "<mark>").replace(/&lt;\/mark&gt;/g, "</mark>"))
/* globals DiscussionUtil, DiscussionContentShowView, MathJax */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.DiscussionThreadShowView = (function(_super) {
__extends(DiscussionThreadShowView, _super);
function DiscussionThreadShowView() {
return DiscussionThreadShowView.__super__.constructor.apply(this, arguments);
}
DiscussionThreadShowView.prototype.initialize = function(options) {
var _ref;
DiscussionThreadShowView.__super__.initialize.call(this);
this.mode = options.mode || "inline";
if ((_ref = this.mode) !== "tab" && _ref !== "inline") {
throw new Error("invalid mode: " + this.mode);
}
};
DiscussionThreadShowView.prototype.renderTemplate = function() {
var context;
this.template = _.template($("#thread-show-template").html());
context = $.extend({
mode: this.mode,
flagged: this.model.isFlagged(),
author_display: this.getAuthorDisplay(),
cid: this.model.cid,
readOnly: $('.discussion-module').data('read-only')
}, this.model.attributes);
return this.template(context);
};
DiscussionThreadShowView.prototype.render = function() {
this.$el.html(this.renderTemplate());
this.delegateEvents();
this.renderAttrs();
this.$("span.timeago").timeago();
this.convertMath();
this.highlight(this.$(".post-body"));
this.highlight(this.$("h1,h3"));
return this;
};
DiscussionThreadShowView.prototype.convertMath = function() {
var element;
element = this.$(".post-body");
element.html(DiscussionUtil.postMathJaxProcessor(DiscussionUtil.markdownWithHighlight(element.text())));
if (typeof MathJax !== "undefined" && MathJax !== null) {
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, element[0]]);
}
};
DiscussionThreadShowView.prototype.edit = function(event) {
return this.trigger("thread:edit", event);
};
DiscussionThreadShowView.prototype._delete = function(event) {
return this.trigger("thread:_delete", event);
};
DiscussionThreadShowView.prototype.highlight = function(el) {
if (el.html()) {
return el.html(el.html().replace(/&lt;mark&gt;/g, "<mark>").replace(/&lt;\/mark&gt;/g, "</mark>"));
}
};
return DiscussionThreadShowView;
})(DiscussionContentShowView);
}
}).call(window);
(function(Backbone) {
(function() {
'use strict';
if (Backbone) {
this.DiscussionTopicMenuView = Backbone.View.extend({
......@@ -42,7 +42,9 @@
this.selectedTopic = this.$('.js-selected-topic');
this.hideTopicDropdown();
if (this.getCurrentTopicId()) {
this.setTopic(this.$('a.topic-title').filter('[data-discussion-id="' + this.getCurrentTopicId() + '"]'));
this.setTopic(this.$('a.topic-title').filter(
'[data-discussion-id="' + this.getCurrentTopicId() + '"]')
);
} else {
this.setTopic(this.$('a.topic-title').first());
}
......@@ -174,7 +176,7 @@
// TODO: this helper class duplicates functionality in DiscussionThreadListView.filterTopics
// for use with a very similar category dropdown in the New Post form. The two menus' implementations
// should be merged into a single reusable view.
filterDrop: function (e) {
filterDrop: function(e) {
var $drop, $items, query;
$drop = $(e.target).parents('.topic-menu-wrapper');
query = $(e.target).val();
......@@ -186,14 +188,14 @@
}
$items.addClass('hidden');
$items.each(function (_index, item) {
$items.each(function(_index, item) {
var path, pathText, pathTitles;
path = $(item).parents(".topic-menu-item").andSelf();
pathTitles = path.children(".topic-title").map(function (_, elem) {
pathTitles = path.children(".topic-title").map(function(_, elem) {
return $(elem).text();
}).get();
pathText = pathTitles.join(" / ").toLowerCase();
if (query.split(" ").every(function (term) {
if (query.split(" ").every(function(term) {
return pathText.search(term.toLowerCase()) !== -1;
})) {
$(item).removeClass('hidden');
......@@ -204,4 +206,4 @@
}
});
}
}).call(this, Backbone);
}).call(this);
if Backbone?
class @DiscussionUserProfileView extends Backbone.View
events:
"click .discussion-paginator a": "changePage"
initialize: (options) ->
super()
@page = options.page
@numPages = options.numPages
@discussion = new Discussion()
@discussion.on("reset", @render)
@discussion.reset(@collection, {silent: false})
render: () =>
@$el.html(_.template($("#user-profile-template").html())({threads: @discussion.models}))
@discussion.map (thread) ->
new DiscussionThreadProfileView(el: @$("article#thread_#{thread.id}"), model: thread).render()
baseUri = URI(window.location).removeSearch("page")
pageUrlFunc = (page) -> baseUri.clone().addSearch("page", page)
paginationParams = DiscussionUtil.getPaginationParams(@page, @numPages, pageUrlFunc)
@$el.find(".discussion-pagination").html(_.template($("#pagination-template").html())(paginationParams))
changePage: (event) ->
event.preventDefault()
url = $(event.target).attr("href")
DiscussionUtil.safeAjax
$elem: @$el
$loading: $(event.target)
takeFocus: true
url: url
type: "GET"
dataType: "json"
success: (response, textStatus, xhr) =>
@page = response.page
@numPages = response.num_pages
@discussion.reset(response.discussion_data, {silent: false})
history.pushState({}, "", url)
$("html, body").animate({ scrollTop: 0 });
error: =>
DiscussionUtil.discussionAlert(
gettext("Sorry"),
gettext("We had some trouble loading the page you requested. Please try again.")
)
/* globals Discussion, DiscussionThreadProfileView, DiscussionUtil, URI */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.DiscussionUserProfileView = (function(_super) {
__extends(DiscussionUserProfileView, _super);
function DiscussionUserProfileView() {
var self = this;
this.render = function() {
return DiscussionUserProfileView.prototype.render.apply(self, arguments);
};
return DiscussionUserProfileView.__super__.constructor.apply(this, arguments);
}
DiscussionUserProfileView.prototype.events = {
"click .discussion-paginator a": "changePage"
};
DiscussionUserProfileView.prototype.initialize = function(options) {
DiscussionUserProfileView.__super__.initialize.call(this);
this.page = options.page;
this.numPages = options.numPages;
this.discussion = new Discussion();
this.discussion.on("reset", this.render);
return this.discussion.reset(this.collection, {
silent: false
});
};
DiscussionUserProfileView.prototype.render = function() {
var baseUri, pageUrlFunc, paginationParams,
self = this;
this.$el.html(_.template($("#user-profile-template").html())({
threads: this.discussion.models
}));
this.discussion.map(function(thread) {
return new DiscussionThreadProfileView({
el: self.$("article#thread_" + thread.id),
model: thread
}).render();
});
baseUri = URI(window.location).removeSearch("page");
pageUrlFunc = function(page) {
return baseUri.clone().addSearch("page", page);
};
paginationParams = DiscussionUtil.getPaginationParams(this.page, this.numPages, pageUrlFunc);
this.$el.find(".discussion-pagination")
.html(_.template($("#pagination-template").html())(paginationParams));
};
DiscussionUserProfileView.prototype.changePage = function(event) {
var url,
self = this;
event.preventDefault();
url = $(event.target).attr("href");
return DiscussionUtil.safeAjax({
$elem: this.$el,
$loading: $(event.target),
takeFocus: true,
url: url,
type: "GET",
dataType: "json",
success: function(response) {
self.page = response.page;
self.numPages = response.num_pages;
self.discussion.reset(response.discussion_data, {
silent: false
});
history.pushState({}, "", url);
return $("html, body").animate({
scrollTop: 0
});
},
error: function() {
return DiscussionUtil.discussionAlert(
gettext("Sorry"),
gettext("We had some trouble loading the page you requested. Please try again.")
);
}
});
};
return DiscussionUserProfileView;
})(Backbone.View);
}
}).call(window);
if Backbone?
class @ResponseCommentEditView extends Backbone.View
/* globals DiscussionUtil */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
events:
"click .post-update": "update"
"click .post-cancel": "cancel_edit"
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
$: (selector) ->
@$el.find(selector)
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.ResponseCommentEditView = (function(_super) {
initialize: ->
super()
__extends(ResponseCommentEditView, _super);
render: ->
@template = _.template($("#response-comment-edit-template").html())
@$el.html(@template(@model.toJSON()))
@delegateEvents()
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "edit-comment-body"
@
function ResponseCommentEditView() {
return ResponseCommentEditView.__super__.constructor.apply(this, arguments);
}
update: (event) ->
@trigger "comment:update", event
ResponseCommentEditView.prototype.events = {
"click .post-update": "update",
"click .post-cancel": "cancel_edit"
};
cancel_edit: (event) ->
@trigger "comment:cancel_edit", event
ResponseCommentEditView.prototype.$ = function(selector) {
return this.$el.find(selector);
};
ResponseCommentEditView.prototype.initialize = function() {
return ResponseCommentEditView.__super__.initialize.call(this);
};
ResponseCommentEditView.prototype.render = function() {
this.template = _.template($("#response-comment-edit-template").html());
this.$el.html(this.template(this.model.toJSON()));
this.delegateEvents();
DiscussionUtil.makeWmdEditor(this.$el, $.proxy(this.$, this), "edit-comment-body");
return this;
};
ResponseCommentEditView.prototype.update = function(event) {
return this.trigger("comment:update", event);
};
ResponseCommentEditView.prototype.cancel_edit = function(event) {
return this.trigger("comment:cancel_edit", event);
};
return ResponseCommentEditView;
})(Backbone.View);
}
}).call(window);
if Backbone?
class @ResponseCommentShowView extends DiscussionContentShowView
tagName: "li"
render: ->
@template = _.template($("#response-comment-show-template").html())
@$el.html(
@template(
_.extend(
{
cid: @model.cid,
author_display: @getAuthorDisplay(),
readOnly: $('.discussion-module').data('read-only')
},
@model.attributes
)
)
)
@delegateEvents()
@renderAttrs()
@$el.find(".timeago").timeago()
@convertMath()
@addReplyLink()
@
addReplyLink: () ->
if @model.hasOwnProperty('parent')
name = @model.parent.get('username') ? gettext("anonymous")
html = "<a href='#comment_#{@model.parent.id}'>@#{name}</a>: "
p = @$('.response-body p:first')
p.prepend(html)
convertMath: ->
body = @$el.find(".response-body")
body.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight body.text()
if MathJax?
MathJax.Hub.Queue ["Typeset", MathJax.Hub, body[0]]
_delete: (event) =>
@trigger "comment:_delete", event
edit: (event) =>
@trigger "comment:edit", event
/* globals DiscussionContentShowView, DiscussionUtil, MathJax */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.ResponseCommentShowView = (function(_super) {
__extends(ResponseCommentShowView, _super);
function ResponseCommentShowView() {
var self = this;
this.edit = function() {
return ResponseCommentShowView.prototype.edit.apply(self, arguments);
};
this._delete = function() {
return ResponseCommentShowView.prototype._delete.apply(self, arguments);
};
return ResponseCommentShowView.__super__.constructor.apply(this, arguments);
}
ResponseCommentShowView.prototype.tagName = "li";
ResponseCommentShowView.prototype.render = function() {
this.template = _.template($("#response-comment-show-template").html());
this.$el.html(this.template(_.extend({
cid: this.model.cid,
author_display: this.getAuthorDisplay(),
readOnly: $('.discussion-module').data('read-only')
}, this.model.attributes)));
this.delegateEvents();
this.renderAttrs();
this.$el.find(".timeago").timeago();
this.convertMath();
this.addReplyLink();
return this;
};
ResponseCommentShowView.prototype.addReplyLink = function() {
var html, name, p, _ref;
if (this.model.hasOwnProperty('parent')) {
name = (_ref = this.model.parent.get('username')) !== null ? _ref : gettext("anonymous");
html = "<a href='#comment_" + this.model.parent.id + "'>@" + name + "</a>: ";
p = this.$('.response-body p:first');
return p.prepend(html);
}
};
ResponseCommentShowView.prototype.convertMath = function() {
var body;
body = this.$el.find(".response-body");
body.html(DiscussionUtil.postMathJaxProcessor(DiscussionUtil.markdownWithHighlight(body.text())));
if (typeof MathJax !== "undefined" && MathJax !== null) {
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, body[0]]);
}
};
ResponseCommentShowView.prototype._delete = function(event) {
return this.trigger("comment:_delete", event);
};
ResponseCommentShowView.prototype.edit = function(event) {
return this.trigger("comment:edit", event);
};
return ResponseCommentShowView;
})(DiscussionContentShowView);
}
}).call(window);
if Backbone?
class @ResponseCommentView extends DiscussionContentView
tagName: "li"
$: (selector) ->
@$el.find(selector)
initialize: ->
super()
render: ->
@renderShowView()
@
renderSubView: (view) ->
view.setElement(@$el)
view.render()
view.delegateEvents()
renderShowView: () ->
if not @showView?
if @editView?
@editView.undelegateEvents()
@editView.$el.empty()
@editView = null
@showView = new ResponseCommentShowView(model: @model)
@showView.bind "comment:_delete", @_delete
@showView.bind "comment:edit", @edit
@renderSubView(@showView)
renderEditView: () ->
if not @editView?
if @showView?
@showView.undelegateEvents()
@showView.$el.empty()
@showView = null
@editView = new ResponseCommentEditView(model: @model)
@editView.bind "comment:update", @update
@editView.bind "comment:cancel_edit", @cancelEdit
@renderSubView(@editView)
_delete: (event) =>
event.preventDefault()
if not @model.can('can_delete')
return
if not confirm gettext("Are you sure you want to delete this comment?")
return
url = @model.urlFor('_delete')
$elem = $(event.target)
DiscussionUtil.safeAjax
$elem: $elem
url: url
type: "POST"
success: (response, textStatus) =>
@model.remove()
@$el.remove()
error: =>
DiscussionUtil.discussionAlert(
gettext("Sorry"),
gettext("We had some trouble deleting this comment. Please try again.")
)
cancelEdit: (event) =>
@trigger "comment:cancel_edit", event
@renderShowView()
edit: (event) =>
@trigger "comment:edit", event
@renderEditView()
update: (event) =>
newBody = @editView.$(".edit-comment-body textarea").val()
url = DiscussionUtil.urlFor("update_comment", @model.id)
DiscussionUtil.safeAjax
$elem: $(event.target)
$loading: $(event.target)
url: url
type: "POST"
dataType: "json"
data:
body: newBody
error: DiscussionUtil.formErrorHandler(@$(".edit-comment-form-errors"))
success: (response, textStatus) =>
@model.set("body", newBody)
@cancelEdit()
/* globals DiscussionContentView, DiscussionUtil, ResponseCommentEditView, ResponseCommentShowView */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.ResponseCommentView = (function(_super) {
__extends(ResponseCommentView, _super);
function ResponseCommentView() {
var self = this;
this.update = function() {
return ResponseCommentView.prototype.update.apply(self, arguments);
};
this.edit = function() {
return ResponseCommentView.prototype.edit.apply(self, arguments);
};
this.cancelEdit = function() {
return ResponseCommentView.prototype.cancelEdit.apply(self, arguments);
};
this._delete = function() {
return ResponseCommentView.prototype._delete.apply(self, arguments);
};
return ResponseCommentView.__super__.constructor.apply(this, arguments);
}
ResponseCommentView.prototype.tagName = "li";
ResponseCommentView.prototype.$ = function(selector) {
return this.$el.find(selector);
};
ResponseCommentView.prototype.initialize = function() {
return ResponseCommentView.__super__.initialize.call(this);
};
ResponseCommentView.prototype.render = function() {
this.renderShowView();
return this;
};
ResponseCommentView.prototype.renderSubView = function(view) {
view.setElement(this.$el);
view.render();
return view.delegateEvents();
};
ResponseCommentView.prototype.renderShowView = function() {
if (!this.showView) {
if (this.editView) {
this.editView.undelegateEvents();
this.editView.$el.empty();
this.editView = null;
}
this.showView = new ResponseCommentShowView({
model: this.model
});
this.showView.bind("comment:_delete", this._delete);
this.showView.bind("comment:edit", this.edit);
return this.renderSubView(this.showView);
}
};
ResponseCommentView.prototype.renderEditView = function() {
if (!this.editView) {
if (this.showView) {
this.showView.undelegateEvents();
this.showView.$el.empty();
this.showView = null;
}
this.editView = new ResponseCommentEditView({
model: this.model
});
this.editView.bind("comment:update", this.update);
this.editView.bind("comment:cancel_edit", this.cancelEdit);
return this.renderSubView(this.editView);
}
};
ResponseCommentView.prototype._delete = function(event) {
var $elem, url,
self = this;
event.preventDefault();
if (!this.model.can('can_delete')) {
return;
}
if (!confirm(gettext("Are you sure you want to delete this comment?"))) {
return;
}
url = this.model.urlFor('_delete');
$elem = $(event.target);
return DiscussionUtil.safeAjax({
$elem: $elem,
url: url,
type: "POST",
success: function() {
self.model.remove();
return self.$el.remove();
},
error: function() {
return DiscussionUtil.discussionAlert(
gettext("Sorry"),
gettext("We had some trouble deleting this comment. Please try again.")
);
}
});
};
ResponseCommentView.prototype.cancelEdit = function(event) {
this.trigger("comment:cancel_edit", event);
return this.renderShowView();
};
ResponseCommentView.prototype.edit = function(event) {
this.trigger("comment:edit", event);
return this.renderEditView();
};
ResponseCommentView.prototype.update = function(event) {
var newBody, url,
self = this;
newBody = this.editView.$(".edit-comment-body textarea").val();
url = DiscussionUtil.urlFor("update_comment", this.model.id);
return DiscussionUtil.safeAjax({
$elem: $(event.target),
$loading: $(event.target),
url: url,
type: "POST",
dataType: "json",
data: {
body: newBody
},
error: DiscussionUtil.formErrorHandler(this.$(".edit-comment-form-errors")),
success: function() {
self.model.set("body", newBody);
return self.cancelEdit();
}
});
};
return ResponseCommentView;
})(DiscussionContentView);
}
}).call(window);
if Backbone?
class @ThreadResponseEditView extends Backbone.View
/* globals DiscussionUtil */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
events:
"click .post-update": "update"
"click .post-cancel": "cancel_edit"
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
$: (selector) ->
@$el.find(selector)
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.ThreadResponseEditView = (function(_super) {
initialize: ->
super()
__extends(ThreadResponseEditView, _super);
render: ->
@template = _.template($("#thread-response-edit-template").html())
@$el.html(@template(@model.toJSON()))
@delegateEvents()
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "edit-post-body"
@
function ThreadResponseEditView() {
return ThreadResponseEditView.__super__.constructor.apply(this, arguments);
}
update: (event) ->
@trigger "response:update", event
ThreadResponseEditView.prototype.events = {
"click .post-update": "update",
"click .post-cancel": "cancel_edit"
};
cancel_edit: (event) ->
@trigger "response:cancel_edit", event
ThreadResponseEditView.prototype.$ = function(selector) {
return this.$el.find(selector);
};
ThreadResponseEditView.prototype.initialize = function() {
return ThreadResponseEditView.__super__.initialize.call(this);
};
ThreadResponseEditView.prototype.render = function() {
this.template = _.template($("#thread-response-edit-template").html());
this.$el.html(this.template(this.model.toJSON()));
this.delegateEvents();
DiscussionUtil.makeWmdEditor(this.$el, $.proxy(this.$, this), "edit-post-body");
return this;
};
ThreadResponseEditView.prototype.update = function(event) {
return this.trigger("response:update", event);
};
ThreadResponseEditView.prototype.cancel_edit = function(event) {
return this.trigger("response:cancel_edit", event);
};
return ThreadResponseEditView;
})(Backbone.View);
}
}).call(window);
if Backbone?
class @ThreadResponseShowView extends DiscussionContentShowView
initialize: ->
super()
@listenTo(@model, "change", @render)
renderTemplate: ->
@template = _.template($("#thread-response-show-template").html())
context = _.extend(
{
cid: @model.cid,
author_display: @getAuthorDisplay(),
endorser_display: @getEndorserDisplay(),
readOnly: $('.discussion-module').data('read-only')
},
@model.attributes
)
@template(context)
render: ->
@$el.html(@renderTemplate())
@delegateEvents()
@renderAttrs()
@$el.find(".posted-details .timeago").timeago()
@convertMath()
@
convertMath: ->
element = @$(".response-body")
element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.text()
if MathJax?
MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]]
edit: (event) ->
@trigger "response:edit", event
_delete: (event) ->
@trigger "response:_delete", event
/* globals DiscussionContentShowView, DiscussionUtil, MathJax */
(function() {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== "undefined" && Backbone !== null) {
this.ThreadResponseShowView = (function(_super) {
__extends(ThreadResponseShowView, _super);
function ThreadResponseShowView() {
return ThreadResponseShowView.__super__.constructor.apply(this, arguments);
}
ThreadResponseShowView.prototype.initialize = function() {
ThreadResponseShowView.__super__.initialize.call(this);
return this.listenTo(this.model, "change", this.render);
};
ThreadResponseShowView.prototype.renderTemplate = function() {
var context;
this.template = _.template($("#thread-response-show-template").html());
context = _.extend({
cid: this.model.cid,
author_display: this.getAuthorDisplay(),
endorser_display: this.getEndorserDisplay(),
readOnly: $('.discussion-module').data('read-only')
}, this.model.attributes);
return this.template(context);
};
ThreadResponseShowView.prototype.render = function() {
this.$el.html(this.renderTemplate());
this.delegateEvents();
this.renderAttrs();
this.$el.find(".posted-details .timeago").timeago();
this.convertMath();
return this;
};
ThreadResponseShowView.prototype.convertMath = function() {
var element;
element = this.$(".response-body");
element.html(DiscussionUtil.postMathJaxProcessor(DiscussionUtil.markdownWithHighlight(element.text())));
if (typeof MathJax !== "undefined" && MathJax !== null) {
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, element[0]]);
}
};
ThreadResponseShowView.prototype.edit = function(event) {
return this.trigger("response:edit", event);
};
ThreadResponseShowView.prototype._delete = function(event) {
return this.trigger("response:_delete", event);
};
return ThreadResponseShowView;
})(DiscussionContentShowView);
}
}).call(window);
......@@ -49,6 +49,7 @@ var commonFiles = {
{pattern: 'edx-pattern-library/js/**/*.js'},
{pattern: 'edx-ui-toolkit/js/**/*.js'},
{pattern: 'xmodule_js/common_static/coffee/src/**/!(*spec).js'},
{pattern: 'xmodule_js/common_static/common/js/**/!(*spec).js'},
{pattern: 'xmodule_js/common_static/js/**/!(*spec).js'},
{pattern: 'xmodule_js/src/**/*.js'}
],
......
describe 'DiscussionUtil', ->
beforeEach ->
DiscussionSpecHelper.setUpGlobals()
/* globals DiscussionSpecHelper, DiscussionUtil */
(function() {
'use strict';
describe('DiscussionUtil', function() {
beforeEach(function() {
return DiscussionSpecHelper.setUpGlobals();
});
return describe("updateWithUndo", function() {
it("calls through to safeAjax with correct params, and reverts the model in case of failure", function() {
var deferred, model, res, updates;
deferred = $.Deferred();
spyOn($, "ajax").and.returnValue(deferred);
spyOn(DiscussionUtil, "safeAjax").and.callThrough();
model = new Backbone.Model({
hello: false,
number: 42
});
updates = {
hello: "world"
};
res = DiscussionUtil.updateWithUndo(model, updates, {
foo: "bar"
}, "error message");
expect(DiscussionUtil.safeAjax).toHaveBeenCalled();
expect(model.attributes).toEqual({
hello: "world",
number: 42
});
spyOn(DiscussionUtil, "discussionAlert");
DiscussionUtil.safeAjax.calls.mostRecent().args[0].error();
expect(DiscussionUtil.discussionAlert).toHaveBeenCalledWith("Sorry", "error message");
deferred.reject();
return expect(model.attributes).toEqual({
hello: false,
number: 42
});
});
return it("rolls back the changes if the associated element is disabled", function() {
var $elem, failed, model, res, updates;
spyOn(DiscussionUtil, "safeAjax").and.callThrough();
model = new Backbone.Model({
hello: false,
number: 42
});
updates = {
hello: "world"
};
$elem = jasmine.createSpyObj('$elem', ['attr']);
$elem.attr.and.returnValue(true);
res = DiscussionUtil.updateWithUndo(model, updates, {
foo: "bar",
$elem: $elem
}, "error message");
expect($elem.attr).toHaveBeenCalledWith("disabled");
expect(DiscussionUtil.safeAjax).toHaveBeenCalled();
expect(model.attributes).toEqual({
hello: false,
number: 42
});
failed = false;
res.fail(function() {
failed = true;
});
return expect(failed).toBe(true);
});
});
});
describe "updateWithUndo", ->
it "calls through to safeAjax with correct params, and reverts the model in case of failure", ->
deferred = $.Deferred()
spyOn($, "ajax").and.returnValue(deferred)
spyOn(DiscussionUtil, "safeAjax").and.callThrough()
model = new Backbone.Model({hello: false, number: 42})
updates = {hello: "world"}
# the ajax request should fire and the model should be updated
res = DiscussionUtil.updateWithUndo(model, updates, {foo: "bar"}, "error message")
expect(DiscussionUtil.safeAjax).toHaveBeenCalled()
expect(model.attributes).toEqual({hello: "world", number: 42})
# the error message callback should be set up correctly
spyOn(DiscussionUtil, "discussionAlert")
DiscussionUtil.safeAjax.calls.mostRecent().args[0].error()
expect(DiscussionUtil.discussionAlert).toHaveBeenCalledWith("Sorry", "error message")
# if the ajax call ends in failure, the model state should be reverted
deferred.reject()
expect(model.attributes).toEqual({hello: false, number: 42})
it "rolls back the changes if the associated element is disabled", ->
spyOn(DiscussionUtil, "safeAjax").and.callThrough()
model = new Backbone.Model({hello: false, number: 42})
updates = {hello: "world"}
# This is the element that is disabled/enabled while the ajax request is
# in progress
$elem = jasmine.createSpyObj('$elem', ['attr'])
$elem.attr.and.returnValue(true)
res = DiscussionUtil.updateWithUndo(model, updates, {foo: "bar", $elem:$elem}, "error message")
expect($elem.attr).toHaveBeenCalledWith("disabled")
expect(DiscussionUtil.safeAjax).toHaveBeenCalled()
expect(model.attributes).toEqual({hello: false, number: 42})
failed = false
res.fail(() => failed = true)
expect(failed).toBe(true);
}).call(this);
describe "DiscussionContentView", ->
beforeEach ->
DiscussionSpecHelper.setUpGlobals()
DiscussionSpecHelper.setUnderscoreFixtures()
/* globals DiscussionSpecHelper, DiscussionContentView, Thread */
(function() {
'use strict';
describe("DiscussionContentView", function() {
beforeEach(function() {
DiscussionSpecHelper.setUpGlobals();
DiscussionSpecHelper.setUnderscoreFixtures();
this.threadData = {
id: '01234567',
user_id: '567',
course_id: 'edX/999/test',
body: 'this is a thread',
created_at: '2013-04-03T20:08:39Z',
abuse_flaggers: ['123'],
votes: {
up_count: '42'
},
type: "thread",
roles: []
};
this.thread = new Thread(this.threadData);
this.view = new DiscussionContentView({
model: this.thread
});
this.view.setElement($('#fixture-element'));
return this.view.render();
});
@threadData = {
id: '01234567',
user_id: '567',
course_id: 'edX/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($('#fixture-element'))
@view.render()
it('defines the tag', function() {
expect($('#jasmine-fixtures')).toExist();
expect(this.view.tagName).toBeDefined();
return expect(this.view.el.tagName.toLowerCase()).toBe('div');
});
it 'defines the tag', ->
expect($('#jasmine-fixtures')).toExist
expect(@view.tagName).toBeDefined
expect(@view.el.tagName.toLowerCase()).toBe 'div'
it("defines the class", function() {
return expect(this.view.model).toBeDefined();
});
it "defines the class", ->
# spyOn @content, 'initialize'
expect(@view.model).toBeDefined();
it('is tied to the model', function() {
return expect(this.view.model).toBeDefined();
});
it 'is tied to the model', ->
expect(@view.model).toBeDefined();
it('can be flagged for abuse', function() {
this.thread.flagAbuse();
return expect(this.thread.get('abuse_flaggers')).toEqual(['123', '567']);
});
it 'can be flagged for abuse', ->
@thread.flagAbuse()
expect(@thread.get 'abuse_flaggers').toEqual ['123', '567']
it 'can be unflagged for abuse', ->
temp_array = []
temp_array.push(window.user.get('id'))
@thread.set("abuse_flaggers",temp_array)
@thread.unflagAbuse()
expect(@thread.get 'abuse_flaggers').toEqual []
it('can be unflagged for abuse', function() {
var temp_array;
temp_array = [];
temp_array.push(window.user.get('id'));
this.thread.set("abuse_flaggers", temp_array);
this.thread.unflagAbuse();
return expect(this.thread.get('abuse_flaggers')).toEqual([]);
});
});
}).call(this);
/* globals
DiscussionCourseSettings, DiscussionSpecHelper, DiscussionThreadEditView, DiscussionUtil,
DiscussionViewSpecHelper, Thread
*/
(function() {
'use strict';
describe('DiscussionThreadEditView', function() {
......@@ -14,7 +18,7 @@
this.thread = new Thread(this.threadData);
this.course_settings = DiscussionSpecHelper.makeCourseSettings();
this.createEditView = function (options) {
this.createEditView = function(options) {
options = _.extend({
container: $('#fixture-element'),
model: this.thread,
......@@ -33,10 +37,13 @@
expect(params.data.commentable_id).toBe(newTopicId);
expect(params.data.title).toBe('changed thread title');
params.success();
return {always: function() {}};
return {
always: function() {
}
};
});
view.$el.find('a.topic-title').filter(function (idx, el) {
view.$el.find('a.topic-title').filter(function(idx, el) {
return $(el).data('discussionId') === newTopicId;
}).click(); // set new topic
view.$('.edit-post-title').val('changed thread title'); // set new title
......@@ -76,6 +83,7 @@
this.createEditView({"mode": "inline"});
testCancel(this.view);
});
describe('renderComments', function() {
beforeEach(function() {
this.course_settings = new DiscussionCourseSettings({
......@@ -100,21 +108,21 @@
'is_cohorted': true
});
});
it('can save new data correctly for current discussion id without dots', function () {
it('can save new data correctly for current discussion id without dots', function() {
this.createEditView({topicId: "topic"});
testUpdate(this.view, this.thread, "6.00.1x_General", "General");
});
it('can save new data correctly for current discussion id with dots', function () {
it('can save new data correctly for current discussion id with dots', function() {
this.createEditView({topicId: "6.00.1x_General"});
testUpdate(this.view, this.thread, "6>00\'1x\"Basic_Question", "Basic Question");
});
it('can save new data correctly for current discussion id with special characters', function () {
it('can save new data correctly for current discussion id with special characters', function() {
this.createEditView({topicId: "6>00\'1x\"Basic_Question"});
testUpdate(this.view, this.thread, "6.00.1x_General", "General");
});
});
});
}).call(this);
/* globals DiscussionTopicMenuView, DiscussionSpecHelper, DiscussionCourseSettings */
(function() {
'use strict';
describe('DiscussionTopicMenuView', function() {
beforeEach(function() {
this.createTopicView = function (options) {
this.createTopicView = function(options) {
options = _.extend({
course_settings: this.course_settings,
topicId: void 0
......@@ -12,14 +13,14 @@
this.defaultTextWidth = this.completeText.length;
};
this.openMenu = function () {
this.openMenu = function() {
var menuWrapper = this.view.$('.topic-menu-wrapper');
expect(menuWrapper).toBeHidden();
this.view.$el.find('.post-topic-button').first().click();
expect(menuWrapper).toBeVisible();
};
this.closeMenu = function () {
this.closeMenu = function() {
var menuWrapper = this.view.$('.topic-menu-wrapper');
expect(menuWrapper).toBeVisible();
this.view.$el.find('.post-topic-button').first().click();
......@@ -76,7 +77,7 @@
expect(dropdownText.indexOf('/ span>')).toEqual(-1);
});
it('appropriate topic is selected if `topicId` is passed', function () {
it('appropriate topic is selected if `topicId` is passed', function() {
var completeText = this.parentCategoryText + ' / Numerical Input',
dropdownText;
this.createTopicView({
......@@ -88,14 +89,14 @@
expect(completeText).toEqual(dropdownText);
});
it('click outside of the dropdown close it', function () {
it('click outside of the dropdown close it', function() {
this.createTopicView();
this.openMenu();
$(document.body).click();
expect(this.view.$('.topic-menu-wrapper')).toBeHidden();
});
it('can toggle the menu', function () {
it('can toggle the menu', function() {
this.createTopicView();
this.openMenu();
this.closeMenu();
......
class @DiscussionViewSpecHelper
@makeThreadWithProps = (props) ->
# Minimal set of properties necessary for rendering
thread = {
id: "dummy_id",
thread_type: "discussion",
pinned: false,
endorsed: false,
votes: {up_count: '0'},
read: false,
unread_comments_count: 0,
comments_count: 0,
abuse_flaggers: [],
body: "",
title: "dummy title",
created_at: "2014-08-18T01:02:03Z"
ability: {
can_delete: false,
can_reply: true,
can_vote: false,
editable: false,
/* globals DiscussionUtil */
(function() {
'use strict';
var __indexOf = [].indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
};
this.DiscussionViewSpecHelper = (function() {
var triggerVoteEvent;
function DiscussionViewSpecHelper() {
}
$.extend(thread, props)
@checkVoteClasses = (view) ->
view.render()
display_button = view.$el.find(".display-vote")
expect(display_button.hasClass("is-hidden")).toBe(true)
action_button = view.$el.find(".action-vote")
# Check that inline css is not applied to the ".action-vote"
expect(action_button).not.toHaveAttr('style','display: inline; ');
DiscussionViewSpecHelper.makeThreadWithProps = function(props) {
var thread;
thread = {
id: "dummy_id",
thread_type: "discussion",
pinned: false,
endorsed: false,
votes: {
up_count: '0'
},
read: false,
unread_comments_count: 0,
comments_count: 0,
abuse_flaggers: [],
body: "",
title: "dummy title",
created_at: "2014-08-18T01:02:03Z",
ability: {
can_delete: false,
can_reply: true,
can_vote: false,
editable: false
}
};
return $.extend(thread, props);
};
DiscussionViewSpecHelper.checkVoteClasses = function(view) {
var action_button, display_button;
view.render();
display_button = view.$el.find(".display-vote");
expect(display_button.hasClass("is-hidden")).toBe(true);
action_button = view.$el.find(".action-vote");
return expect(action_button).not.toHaveAttr('style', 'display: inline; ');
};
DiscussionViewSpecHelper.expectVoteRendered = function(view, model, user) {
var button;
button = view.$el.find(".action-vote");
expect(button.hasClass("is-checked")).toBe(user.voted(model));
expect(button.attr("aria-checked")).toEqual(user.voted(model).toString());
expect(button.find(".vote-count").text()).toMatch("^" + (model.get('votes').up_count) + " Votes?$");
return expect(button.find(".sr.js-sr-vote-count").text())
.toMatch("^there are currently " + (model.get('votes').up_count) + " votes?$");
};
DiscussionViewSpecHelper.checkRenderVote = function(view, model) {
view.render();
DiscussionViewSpecHelper.expectVoteRendered(view, model, window.user);
window.user.vote(model);
view.render();
DiscussionViewSpecHelper.expectVoteRendered(view, model, window.user);
window.user.unvote(model);
view.render();
return DiscussionViewSpecHelper.expectVoteRendered(view, model, window.user);
};
@expectVoteRendered = (view, model, user) ->
button = view.$el.find(".action-vote")
expect(button.hasClass("is-checked")).toBe(user.voted(model))
expect(button.attr("aria-checked")).toEqual(user.voted(model).toString())
expect(button.find(".vote-count").text()).toMatch("^#{model.get('votes').up_count} Votes?$")
expect(button.find(".sr.js-sr-vote-count").text()).toMatch("^there are currently #{model.get('votes').up_count} votes?$")
triggerVoteEvent = function(view, event, expectedUrl) {
var deferred;
deferred = $.Deferred();
spyOn($, "ajax").and.callFake(function(params) {
expect(params.url.toString()).toEqual(expectedUrl);
return deferred;
});
view.render();
view.$el.find(".action-vote").trigger(event);
expect($.ajax).toHaveBeenCalled();
return deferred.resolve();
};
@checkRenderVote = (view, model) ->
view.render()
DiscussionViewSpecHelper.expectVoteRendered(view, model, window.user)
window.user.vote(model)
view.render()
DiscussionViewSpecHelper.expectVoteRendered(view, model, window.user)
window.user.unvote(model)
view.render()
DiscussionViewSpecHelper.expectVoteRendered(view, model, window.user)
DiscussionViewSpecHelper.checkUpvote = function(view, model, user, event) {
var initialVoteCount, _ref, _ref1;
expect((_ref = model.id, __indexOf.call(user.get('upvoted_ids'), _ref) >= 0)).toBe(false);
initialVoteCount = model.get('votes').up_count;
triggerVoteEvent(view, event, DiscussionUtil.urlFor("upvote_" + (model.get('type')), model.id) + "?ajax=1");
expect((_ref1 = model.id, __indexOf.call(user.get('upvoted_ids'), _ref1) >= 0)).toBe(true);
return expect(model.get('votes').up_count).toEqual(initialVoteCount + 1);
};
triggerVoteEvent = (view, event, expectedUrl) ->
deferred = $.Deferred()
spyOn($, "ajax").and.callFake((params) =>
expect(params.url.toString()).toEqual(expectedUrl)
return deferred
)
view.render()
view.$el.find(".action-vote").trigger(event)
expect($.ajax).toHaveBeenCalled()
deferred.resolve()
DiscussionViewSpecHelper.checkUnvote = function(view, model, user, event) {
var initialVoteCount, _ref;
user.vote(model);
expect((_ref = model.id, __indexOf.call(user.get('upvoted_ids'), _ref) >= 0)).toBe(true);
initialVoteCount = model.get('votes').up_count;
triggerVoteEvent(
view, event, DiscussionUtil.urlFor("undo_vote_for_" + (model.get('type')), model.id) + "?ajax=1"
);
expect(user.get('upvoted_ids')).toEqual([]);
return expect(model.get('votes').up_count).toEqual(initialVoteCount - 1);
};
@checkUpvote = (view, model, user, event) ->
expect(model.id in user.get('upvoted_ids')).toBe(false)
initialVoteCount = model.get('votes').up_count
triggerVoteEvent(view, event, DiscussionUtil.urlFor("upvote_#{model.get('type')}", model.id) + "?ajax=1")
expect(model.id in user.get('upvoted_ids')).toBe(true)
expect(model.get('votes').up_count).toEqual(initialVoteCount + 1)
DiscussionViewSpecHelper.checkButtonEvents = function(view, viewFunc, buttonSelector) {
var button, spy;
spy = spyOn(view, viewFunc);
button = view.$el.find(buttonSelector);
button.click();
expect(spy).toHaveBeenCalled();
spy.calls.reset();
button.trigger($.Event("keydown", {
which: 13
}));
expect(spy).not.toHaveBeenCalled();
spy.calls.reset();
button.trigger($.Event("keydown", {
which: 32
}));
return expect(spy).toHaveBeenCalled();
};
@checkUnvote = (view, model, user, event) ->
user.vote(model)
expect(model.id in user.get('upvoted_ids')).toBe(true)
initialVoteCount = model.get('votes').up_count
triggerVoteEvent(view, event, DiscussionUtil.urlFor("undo_vote_for_#{model.get('type')}", model.id) + "?ajax=1")
expect(user.get('upvoted_ids')).toEqual([])
expect(model.get('votes').up_count).toEqual(initialVoteCount - 1)
DiscussionViewSpecHelper.checkVoteButtonEvents = function(view) {
return this.checkButtonEvents(view, "toggleVote", ".action-vote");
};
@checkButtonEvents = (view, viewFunc, buttonSelector) ->
spy = spyOn(view, viewFunc)
button = view.$el.find(buttonSelector)
DiscussionViewSpecHelper.setNextResponseContent = function(content) {
return $.ajax.and.callFake(function(params) {
params.success({
"content": content
});
return {
always: function() {
}
};
});
};
button.click()
expect(spy).toHaveBeenCalled()
spy.calls.reset()
button.trigger($.Event("keydown", {which: 13}))
expect(spy).not.toHaveBeenCalled()
spy.calls.reset()
button.trigger($.Event("keydown", {which: 32}))
expect(spy).toHaveBeenCalled()
return DiscussionViewSpecHelper;
@checkVoteButtonEvents = (view) ->
@checkButtonEvents(view, "toggleVote", ".action-vote")
})();
@setNextResponseContent = (content) ->
$.ajax.and.callFake(
(params) =>
params.success({"content": content})
{always: ->}
)
}).call(this);
describe 'ResponseCommentShowView', ->
beforeEach ->
DiscussionSpecHelper.setUpGlobals()
# set up the container for the response to go in
DiscussionSpecHelper.setUnderscoreFixtures()
# set up a model for a new Comment
@comment = new Comment {
/* globals DiscussionSpecHelper, DiscussionUtil, DiscussionViewSpecHelper, ResponseCommentShowView */
(function() {
'use strict';
describe('ResponseCommentShowView', function() {
beforeEach(function() {
DiscussionSpecHelper.setUpGlobals();
DiscussionSpecHelper.setUnderscoreFixtures();
this.comment = new Comment({
id: '01234567',
user_id: '567',
course_id: 'edX/999/test',
body: 'this is a response',
created_at: '2013-04-03T20:08:39Z',
abuse_flaggers: ['123']
abuse_flaggers: ['123'],
roles: []
}
@view = new ResponseCommentShowView({ model: @comment })
spyOn(@view, "convertMath")
it 'defines the tag', ->
expect($('#jasmine-fixtures')).toExist
expect(@view.tagName).toBeDefined
expect(@view.el.tagName.toLowerCase()).toBe 'li'
it 'is tied to the model', ->
expect(@view.model).toBeDefined()
describe 'rendering', ->
beforeEach ->
spyOn(@view, 'renderAttrs')
it 'can be flagged for abuse', ->
@comment.flagAbuse()
expect(@comment.get 'abuse_flaggers').toEqual ['123', '567']
it 'can be unflagged for abuse', ->
temp_array = []
temp_array.push(window.user.get('id'))
@comment.set("abuse_flaggers",temp_array)
@comment.unflagAbuse()
expect(@comment.get 'abuse_flaggers').toEqual []
describe '_delete', ->
it 'triggers on the correct events', ->
DiscussionUtil.loadRoles []
@comment.updateInfo {ability: {'can_delete': true}}
@view.render()
DiscussionViewSpecHelper.checkButtonEvents(@view, "_delete", ".action-delete")
it 'triggers the delete event', ->
triggerTarget = jasmine.createSpy()
@view.bind "comment:_delete", triggerTarget
@view._delete()
expect(triggerTarget).toHaveBeenCalled()
describe 'edit', ->
it 'triggers on the correct events', ->
DiscussionUtil.loadRoles []
@comment.updateInfo {ability: {'can_edit': true}}
@view.render()
DiscussionViewSpecHelper.checkButtonEvents(@view, "edit", ".action-edit")
it 'triggers comment:edit when the edit button is clicked', ->
triggerTarget = jasmine.createSpy()
@view.bind "comment:edit", triggerTarget
@view.edit()
expect(triggerTarget).toHaveBeenCalled()
describe "labels", ->
expectOneElement = (view, selector, visible=true) =>
view.render()
elements = view.$el.find(selector)
expect(elements.length).toEqual(1)
if visible
expect(elements).not.toHaveClass("is-hidden")
else
expect(elements).toHaveClass("is-hidden")
it 'displays the reported label when appropriate for a non-staff user', ->
@comment.set('abuse_flaggers', [])
expectOneElement(@view, '.post-label-reported', false)
# flagged by current user - should be labelled
@comment.set('abuse_flaggers', [DiscussionUtil.getUser().id])
expectOneElement(@view, '.post-label-reported')
# flagged by some other user but not the current one - should not be labelled
@comment.set('abuse_flaggers', [DiscussionUtil.getUser().id + 1])
expectOneElement(@view, '.post-label-reported', false)
it 'displays the reported label when appropriate for a flag moderator', ->
DiscussionSpecHelper.makeModerator()
@comment.set('abuse_flaggers', [])
expectOneElement(@view, '.post-label-reported', false)
# flagged by current user - should be labelled
@comment.set('abuse_flaggers', [DiscussionUtil.getUser().id])
expectOneElement(@view, '.post-label-reported')
# flagged by some other user but not the current one - should still be labelled
@comment.set('abuse_flaggers', [DiscussionUtil.getUser().id + 1])
expectOneElement(@view, '.post-label-reported')
});
this.view = new ResponseCommentShowView({
model: this.comment
});
return spyOn(this.view, "convertMath");
});
it('defines the tag', function() {
expect($('#jasmine-fixtures')).toExist();
expect(this.view.tagName).toBeDefined();
return expect(this.view.el.tagName.toLowerCase()).toBe('li');
});
it('is tied to the model', function() {
return expect(this.view.model).toBeDefined();
});
describe('rendering', function() {
beforeEach(function() {
return spyOn(this.view, 'renderAttrs');
});
it('can be flagged for abuse', function() {
this.comment.flagAbuse();
return expect(this.comment.get('abuse_flaggers')).toEqual(['123', '567']);
});
it('can be unflagged for abuse', function() {
var temp_array;
temp_array = [];
temp_array.push(window.user.get('id'));
this.comment.set("abuse_flaggers", temp_array);
this.comment.unflagAbuse();
return expect(this.comment.get('abuse_flaggers')).toEqual([]);
});
});
describe('_delete', function() {
it('triggers on the correct events', function() {
DiscussionUtil.loadRoles([]);
this.comment.updateInfo({
ability: {
'can_delete': true
}
});
this.view.render();
return DiscussionViewSpecHelper.checkButtonEvents(this.view, "_delete", ".action-delete");
});
it('triggers the delete event', function() {
var triggerTarget;
triggerTarget = jasmine.createSpy();
this.view.bind("comment:_delete", triggerTarget);
this.view._delete();
return expect(triggerTarget).toHaveBeenCalled();
});
});
describe('edit', function() {
it('triggers on the correct events', function() {
DiscussionUtil.loadRoles([]);
this.comment.updateInfo({
ability: {
'can_edit': true
}
});
this.view.render();
return DiscussionViewSpecHelper.checkButtonEvents(this.view, "edit", ".action-edit");
});
it('triggers comment:edit when the edit button is clicked', function() {
var triggerTarget;
triggerTarget = jasmine.createSpy();
this.view.bind("comment:edit", triggerTarget);
this.view.edit();
return expect(triggerTarget).toHaveBeenCalled();
});
});
describe("labels", function() {
var expectOneElement;
expectOneElement = function(view, selector, visible) {
var elements;
if (typeof visible === "undefined" || visible === null) {
visible = true;
}
view.render();
elements = view.$el.find(selector);
expect(elements.length).toEqual(1);
if (visible) {
return expect(elements).not.toHaveClass("is-hidden");
} else {
return expect(elements).toHaveClass("is-hidden");
}
};
it('displays the reported label when appropriate for a non-staff user', function() {
this.comment.set('abuse_flaggers', []);
expectOneElement(this.view, '.post-label-reported', false);
this.comment.set('abuse_flaggers', [DiscussionUtil.getUser().id]);
expectOneElement(this.view, '.post-label-reported');
this.comment.set('abuse_flaggers', [DiscussionUtil.getUser().id + 1]);
return expectOneElement(this.view, '.post-label-reported', false);
});
it('displays the reported label when appropriate for a flag moderator', function() {
DiscussionSpecHelper.makeModerator();
this.comment.set('abuse_flaggers', []);
expectOneElement(this.view, '.post-label-reported', false);
this.comment.set('abuse_flaggers', [DiscussionUtil.getUser().id]);
expectOneElement(this.view, '.post-label-reported');
this.comment.set('abuse_flaggers', [DiscussionUtil.getUser().id + 1]);
return expectOneElement(this.view, '.post-label-reported');
});
});
});
}).call(this);
describe 'ThreadResponseView', ->
beforeEach ->
DiscussionSpecHelper.setUpGlobals()
DiscussionSpecHelper.setUnderscoreFixtures()
/* globals DiscussionSpecHelper, ResponseCommentView, Thread, ThreadResponseView, ThreadResponseShowView */
(function() {
'use strict';
describe('ThreadResponseView', function() {
beforeEach(function() {
DiscussionSpecHelper.setUpGlobals();
DiscussionSpecHelper.setUnderscoreFixtures();
this.thread = new Thread({
"thread_type": "discussion"
});
this.response = new Comment({
children: [{}, {}],
thread: this.thread
});
this.view = new ThreadResponseView({
model: this.response,
el: $("#fixture-element")
});
spyOn(ThreadResponseShowView.prototype, "render");
return spyOn(ResponseCommentView.prototype, "render");
});
describe('closed and open Threads', function() {
var checkCommentForm;
checkCommentForm = function(closed) {
var comment, commentData, thread, view;
thread = new Thread({
"thread_type": "discussion",
"closed": closed
});
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: [],
type: "comment",
children: [],
thread: thread
};
comment = new Comment(commentData);
view = new ThreadResponseView({
model: comment,
el: $("#fixture-element")
});
view.render();
return expect(view.$('.comment-form').closest('li').is(":visible")).toBe(!closed);
};
it('hides comment form when thread is closed', function() {
return checkCommentForm(true);
});
it('show comment form when thread is open', function() {
return checkCommentForm(false);
});
});
describe('renderComments', function() {
it('hides "show comments" link if collapseComments is not set', function() {
this.view.render();
expect(this.view.$(".comments")).toBeVisible();
return expect(this.view.$(".action-show-comments")).not.toBeVisible();
});
it('hides "show comments" link if collapseComments is set but response has no comments', function() {
this.response = new Comment({
children: [],
thread: this.thread
});
this.view = new ThreadResponseView({
model: this.response,
el: $("#fixture-element"),
collapseComments: true
});
this.view.render();
expect(this.view.$(".comments")).toBeVisible();
return expect(this.view.$(".action-show-comments")).not.toBeVisible();
});
it(
'hides comments if collapseComments is set and shows them when "show comments" link is clicked',
function() {
this.view = new ThreadResponseView({
model: this.response,
el: $("#fixture-element"),
collapseComments: true
});
this.view.render();
expect(this.view.$(".comments")).not.toBeVisible();
expect(this.view.$(".action-show-comments")).toBeVisible();
this.view.$(".action-show-comments").click();
expect(this.view.$(".comments")).toBeVisible();
return expect(this.view.$(".action-show-comments")).not.toBeVisible();
}
);
it('populates commentViews and binds events', function() {
this.view.createEditView();
spyOn(this.view, 'cancelEdit');
spyOn(this.view, 'cancelCommentEdits');
spyOn(this.view, 'hideCommentForm');
spyOn(this.view, 'showCommentForm');
this.view.renderComments();
expect(this.view.commentViews.length).toEqual(2);
this.view.commentViews[0].trigger("comment:edit", jasmine.createSpyObj("event", ["preventDefault"]));
expect(this.view.cancelEdit).toHaveBeenCalled();
expect(this.view.cancelCommentEdits).toHaveBeenCalled();
expect(this.view.hideCommentForm).toHaveBeenCalled();
this.view.commentViews[0].trigger("comment:cancel_edit");
return expect(this.view.showCommentForm).toHaveBeenCalled();
});
});
describe('cancelCommentEdits', function() {
it('calls cancelEdit on each comment view', function() {
this.view.renderComments();
expect(this.view.commentViews.length).toEqual(2);
_.each(this.view.commentViews, function(commentView) {
return spyOn(commentView, 'cancelEdit');
});
this.view.cancelCommentEdits();
return _.each(this.view.commentViews, function(commentView) {
return expect(commentView.cancelEdit).toHaveBeenCalled();
});
});
});
});
@thread = new Thread({"thread_type": "discussion"})
@response = new Comment {
children: [{}, {}],
thread: @thread,
}
@view = new ThreadResponseView({model: @response, el: $("#fixture-element")})
spyOn(ThreadResponseShowView.prototype, "render")
spyOn(ResponseCommentView.prototype, "render")
describe 'closed and open Threads', ->
checkCommentForm = (closed) ->
thread = new Thread({"thread_type": "discussion", "closed": closed})
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: [],
type: "comment",
children: [],
thread: thread,
}
comment = new Comment(commentData)
view = new ThreadResponseView({
model: comment, el: $("#fixture-element"),
})
view.render()
expect(view.$('.comment-form').closest('li').is(":visible")).toBe(not closed)
it 'hides comment form when thread is closed', ->
checkCommentForm(true)
it 'show comment form when thread is open', ->
checkCommentForm(false)
describe 'renderComments', ->
it 'hides "show comments" link if collapseComments is not set', ->
@view.render()
expect(@view.$(".comments")).toBeVisible()
expect(@view.$(".action-show-comments")).not.toBeVisible()
it 'hides "show comments" link if collapseComments is set but response has no comments', ->
@response = new Comment { children: [], thread: @thread }
@view = new ThreadResponseView({
model: @response, el: $("#fixture-element"),
collapseComments: true
})
@view.render()
expect(@view.$(".comments")).toBeVisible()
expect(@view.$(".action-show-comments")).not.toBeVisible()
it 'hides comments if collapseComments is set and shows them when "show comments" link is clicked', ->
@view = new ThreadResponseView({
model: @response, el: $("#fixture-element"),
collapseComments: true
})
@view.render()
expect(@view.$(".comments")).not.toBeVisible()
expect(@view.$(".action-show-comments")).toBeVisible()
@view.$(".action-show-comments").click()
expect(@view.$(".comments")).toBeVisible()
expect(@view.$(".action-show-comments")).not.toBeVisible()
it 'populates commentViews and binds events', ->
# Ensure that edit view is set to test invocation of cancelEdit
@view.createEditView()
spyOn(@view, 'cancelEdit')
spyOn(@view, 'cancelCommentEdits')
spyOn(@view, 'hideCommentForm')
spyOn(@view, 'showCommentForm')
@view.renderComments()
expect(@view.commentViews.length).toEqual(2)
@view.commentViews[0].trigger "comment:edit", jasmine.createSpyObj("event", ["preventDefault"])
expect(@view.cancelEdit).toHaveBeenCalled()
expect(@view.cancelCommentEdits).toHaveBeenCalled()
expect(@view.hideCommentForm).toHaveBeenCalled()
@view.commentViews[0].trigger "comment:cancel_edit"
expect(@view.showCommentForm).toHaveBeenCalled()
describe 'cancelCommentEdits', ->
it 'calls cancelEdit on each comment view', ->
@view.renderComments()
expect(@view.commentViews.length).toEqual(2)
_.each(@view.commentViews, (commentView) -> spyOn(commentView, 'cancelEdit'))
@view.cancelCommentEdits()
_.each(@view.commentViews, (commentView) -> expect(commentView.cancelEdit).toHaveBeenCalled())
}).call(this);
class @DiscussionSpecHelper
# This is sad. We should avoid dependence on global vars.
@setUpGlobals = ->
DiscussionUtil.loadRoles({"Moderator": [], "Administrator": [], "Community TA": []})
window.$$course_id = "edX/999/test"
window.user = new DiscussionUser({username: "test_user", id: "567", upvoted_ids: []})
DiscussionUtil.setUser(window.user)
/* global DiscussionCourseSettings, DiscussionUtil, DiscussionUser */
(function () {
'use strict';
this.DiscussionSpecHelper = (function () {
@makeTA = () ->
DiscussionUtil.roleIds["Community TA"].push(parseInt(DiscussionUtil.getUser().id))
function DiscussionSpecHelper() {
}
@makeModerator = () ->
DiscussionUtil.roleIds["Moderator"].push(parseInt(DiscussionUtil.getUser().id))
DiscussionSpecHelper.setUpGlobals = function () {
DiscussionUtil.loadRoles({
"Moderator": [],
"Administrator": [],
"Community TA": []
});
window.$$course_id = "edX/999/test";
window.user = new DiscussionUser({
username: "test_user",
id: "567",
upvoted_ids: []
});
return DiscussionUtil.setUser(window.user);
};
@makeAjaxSpy = (fakeAjax) ->
spyOn($, "ajax").and.callFake(
(params) ->
fakeAjax(params)
{always: ->}
)
DiscussionSpecHelper.makeTA = function () {
return DiscussionUtil.roleIds["Community TA"].push(parseInt(DiscussionUtil.getUser().id));
};
@makeEventSpy = () ->
jasmine.createSpyObj('event', ['preventDefault', 'target'])
DiscussionSpecHelper.makeModerator = function () {
return DiscussionUtil.roleIds.Moderator.push(parseInt(DiscussionUtil.getUser().id));
};
@makeCourseSettings = (is_cohorted=true) ->
new DiscussionCourseSettings(
category_map:
children: ['Test Topic', 'Other Topic']
entries:
'Test Topic':
is_cohorted: is_cohorted
id: 'test_topic'
'Other Topic':
is_cohorted: is_cohorted
id: 'other_topic'
is_cohorted: is_cohorted
)
DiscussionSpecHelper.makeAjaxSpy = function (fakeAjax) {
return spyOn($, "ajax").and.callFake(function (params) {
fakeAjax(params);
return {
always: function () {
}
};
});
};
@setUnderscoreFixtures = ->
templateNames = [
'thread', 'thread-show', 'thread-edit',
'thread-response', 'thread-response-show', 'thread-response-edit',
'response-comment-show', 'response-comment-edit',
'thread-list-item', 'discussion-home', 'search-alert',
'new-post', 'thread-type', 'new-post-menu-entry',
'new-post-menu-category', 'topic', 'post-user-display',
'inline-discussion', 'pagination', 'user-profile', 'profile-thread'
]
templateNamesNoTrailingTemplate = [
'forum-action-endorse', 'forum-action-answer', 'forum-action-follow',
'forum-action-vote', 'forum-action-report', 'forum-action-pin',
'forum-action-close', 'forum-action-edit', 'forum-action-delete',
'forum-actions',
]
DiscussionSpecHelper.makeEventSpy = function () {
return jasmine.createSpyObj('event', ['preventDefault', 'target']);
};
for templateName in templateNames
templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore')
appendSetFixtures($('<script>', { id: templateName + '-template', type: 'text/template' })
.text(templateFixture))
for templateName in templateNamesNoTrailingTemplate
templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore')
appendSetFixtures($('<script>', { id: templateName, type: 'text/template' })
.text(templateFixture))
appendSetFixtures("""
<div id="fixture-element"></div>
<div id="discussion-container"
data-course-name="Fake Course"
data-user-create-comment="true"
data-user-create-subcomment="true"
data-read-only="false"
></div>
""")
DiscussionSpecHelper.makeCourseSettings = function (is_cohorted) {
if (typeof is_cohorted === 'undefined' || is_cohorted === null) {
is_cohorted = true;
}
return new DiscussionCourseSettings({
category_map: {
children: ['Test Topic', 'Other Topic'],
entries: {
'Test Topic': {
is_cohorted: is_cohorted,
id: 'test_topic'
},
'Other Topic': {
is_cohorted: is_cohorted,
id: 'other_topic'
}
}
},
is_cohorted: is_cohorted
});
};
DiscussionSpecHelper.setUnderscoreFixtures = function () {
var templateFixture, templateName, templateNames, templateNamesNoTrailingTemplate, _i, _j, _len, _len1;
templateNames = [
'thread', 'thread-show', 'thread-edit', 'thread-response', 'thread-response-show',
'thread-response-edit', 'response-comment-show', 'response-comment-edit', 'thread-list-item',
'discussion-home', 'search-alert', 'new-post', 'thread-type', 'new-post-menu-entry',
'new-post-menu-category', 'topic', 'post-user-display', 'inline-discussion', 'pagination',
'user-profile', 'profile-thread'
];
templateNamesNoTrailingTemplate = [
'forum-action-endorse', 'forum-action-answer', 'forum-action-follow', 'forum-action-vote',
'forum-action-report', 'forum-action-pin', 'forum-action-close', 'forum-action-edit',
'forum-action-delete', 'forum-actions'
];
for (_i = 0, _len = templateNames.length; _i < _len; _i++) {
templateName = templateNames[_i];
templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore');
appendSetFixtures($('<script>', {
id: templateName + '-template',
type: 'text/template'
}).text(templateFixture));
}
for (_j = 0, _len1 = templateNamesNoTrailingTemplate.length; _j < _len1; _j++) {
templateName = templateNamesNoTrailingTemplate[_j];
templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore');
appendSetFixtures($('<script>', {
id: templateName,
type: 'text/template'
}).text(templateFixture));
}
return appendSetFixtures(
"<div id=\"fixture-element\"></div>\n" +
"<div id=\"discussion-container\"" +
" data-course-name=\"Fake Course\"" +
" data-user-create-comment=\"true\"" +
" data-user-create-subcomment=\"true\"" +
" data-read-only=\"false\"" +
"></div>"
);
};
return DiscussionSpecHelper;
})();
}).call(this);
define([
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/views/team_discussion',
'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/coffee/spec/discussion/discussion_spec_helper'
'xmodule_js/common_static/common/js/spec_helpers/discussion_spec_helper'
], function (_, AjaxHelpers, TeamDiscussionView, TeamSpecHelpers, DiscussionSpecHelper) {
'use strict';
xdescribe('TeamDiscussionView', function() {
......
define([
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/models/team',
'teams/js/views/team_profile', 'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/coffee/spec/discussion/discussion_spec_helper'
'xmodule_js/common_static/common/js/spec_helpers/discussion_spec_helper'
], function (_, AjaxHelpers, TeamModel, TeamProfileView, TeamSpecHelpers, DiscussionSpecHelper) {
'use strict';
describe('TeamProfileView', function () {
......
......@@ -1296,7 +1296,7 @@ discussion_js = (
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/customwmd.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/mathjax_accessible.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/mathjax_delay_renderer.js') +
sorted(rooted_glob(COMMON_ROOT / 'static', 'coffee/src/discussion/**/*.js'))
sorted(rooted_glob(COMMON_ROOT / 'static', 'common/js/discussion/**/*.js'))
)
discussion_vendor_js = [
......
......@@ -2,18 +2,18 @@
"rules": {
"javascript-concat-html": 205,
"javascript-escape": 7,
"javascript-interpolate": 51,
"javascript-jquery-append": 110,
"javascript-jquery-html": 274,
"javascript-jquery-insert-into-target": 26,
"javascript-jquery-insertion": 28,
"javascript-jquery-prepend": 12,
"javascript-interpolate": 49,
"javascript-jquery-append": 104,
"javascript-jquery-html": 275,
"javascript-jquery-insert-into-target": 27,
"javascript-jquery-insertion": 26,
"javascript-jquery-prepend": 11,
"mako-html-entities": 0,
"mako-invalid-html-filter": 29,
"mako-invalid-js-filter": 209,
"mako-invalid-html-filter": 27,
"mako-invalid-js-filter": 207,
"mako-js-html-string": 0,
"mako-js-missing-quotes": 0,
"mako-missing-default": 215,
"mako-missing-default": 213,
"mako-multiple-page-tags": 0,
"mako-unknown-context": 0,
"mako-unparseable-expression": 0,
......@@ -21,12 +21,12 @@
"python-close-before-format": 0,
"python-concat-html": 27,
"python-custom-escape": 13,
"python-deprecated-display-name": 54,
"python-interpolate-html": 68,
"python-deprecated-display-name": 53,
"python-interpolate-html": 66,
"python-parse-error": 0,
"python-requires-html-or-text": 0,
"python-wrap-html": 266,
"underscore-not-escaped": 659
"python-wrap-html": 264,
"underscore-not-escaped": 658
},
"total": 2237
"total": 2232
}
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