Merge pull request #12914 from open-craft/ekolpakov/discussion_fixes_from_solutions
Discussion fixes from solutions
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* globals DiscussionThreadListView, DiscussionThreadView, DiscussionUtil, NewPostView */
|
||||
/* globals DiscussionThreadListView, DiscussionThreadView, DiscussionUtil, NewPostView, Thread */
|
||||
(function() {
|
||||
'use strict';
|
||||
var __hasProp = {}.hasOwnProperty,
|
||||
@@ -18,9 +18,20 @@
|
||||
return child;
|
||||
};
|
||||
|
||||
function getSingleThreadRoute(commentable_id, thread_id) {
|
||||
return commentable_id + "/threads/" + thread_id;
|
||||
}
|
||||
|
||||
if (typeof Backbone !== "undefined" && Backbone !== null) {
|
||||
this.DiscussionRouter = (function(_super) {
|
||||
|
||||
var allThreadsRoute = "",
|
||||
singleThreadRoute = getSingleThreadRoute(":forum_name", ":thread_id"), // :forum_name/threads/:thread_id
|
||||
routes = {};
|
||||
|
||||
routes[allThreadsRoute] = "allThreads";
|
||||
routes[singleThreadRoute] = "showThread";
|
||||
|
||||
__extends(DiscussionRouter, _super);
|
||||
|
||||
function DiscussionRouter() {
|
||||
@@ -40,16 +51,16 @@
|
||||
this.showMain = function() {
|
||||
return DiscussionRouter.prototype.showMain.apply(self, arguments);
|
||||
};
|
||||
this.renderThreadView = function() {
|
||||
return DiscussionRouter.prototype.renderThreadView.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.routes = routes;
|
||||
|
||||
DiscussionRouter.prototype.initialize = function(options) {
|
||||
var self = this;
|
||||
@@ -82,23 +93,50 @@
|
||||
|
||||
DiscussionRouter.prototype.allThreads = function() {
|
||||
this.nav.updateSidebar();
|
||||
return this.nav.goHome();
|
||||
this.nav.goHome();
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.setActiveThread = function() {
|
||||
if (this.thread) {
|
||||
return this.nav.setActiveThread(this.thread.get("id"));
|
||||
} else {
|
||||
return this.nav.goHome;
|
||||
this.nav.setActiveThread(this.thread.get("id"));
|
||||
}
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.showThread = function(forum_name, thread_id) {
|
||||
var self = this;
|
||||
this.thread = this.discussion.get(thread_id);
|
||||
|
||||
if (this.thread) {
|
||||
this.renderThreadView();
|
||||
return;
|
||||
}
|
||||
|
||||
// if thread is not loaded yet for some reason - try loading it
|
||||
DiscussionUtil.safeAjax({
|
||||
url: DiscussionUtil.urlFor('retrieve_single_thread', forum_name, thread_id)
|
||||
}).done(function(data) {
|
||||
// if succeded - proceed normally
|
||||
self.thread = new Thread(data.content);
|
||||
self.discussion.add(self.thread);
|
||||
self.renderThreadView();
|
||||
}).fail(function(xhr) {
|
||||
// otherwise display error message and navigate to all threads view
|
||||
var errorMsg;
|
||||
if (xhr.status === 404) {
|
||||
errorMsg = gettext("The thread you selected has been deleted. Please select another thread.");
|
||||
} else {
|
||||
errorMsg = gettext("We had some trouble loading more responses. Please try again.");
|
||||
}
|
||||
DiscussionUtil.discussionAlert(gettext("Sorry"), errorMsg);
|
||||
this.allThreads();
|
||||
});
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.renderThreadView = function() {
|
||||
this.thread.set("unread_comments_count", 0);
|
||||
this.thread.set("read", true);
|
||||
this.setActiveThread();
|
||||
return this.showMain();
|
||||
this.showMain();
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.showMain = function() {
|
||||
@@ -123,26 +161,23 @@
|
||||
this.main.on("thread:responses:rendered", function() {
|
||||
return self.nav.updateSidebar();
|
||||
});
|
||||
return this.thread.on("thread:thread_type_updated", this.showMain);
|
||||
this.thread.on("thread:thread_type_updated", this.showMain);
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.navigateToThread = function(thread_id) {
|
||||
var thread;
|
||||
var thread, targetThreadRoute;
|
||||
thread = this.discussion.get(thread_id);
|
||||
return this.navigate("" + (thread.get("commentable_id")) + "/threads/" + thread_id, {
|
||||
trigger: true
|
||||
});
|
||||
targetThreadRoute = getSingleThreadRoute(thread.get("commentable_id"), thread_id);
|
||||
this.navigate(targetThreadRoute, {trigger: true});
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.navigateToAllThreads = function() {
|
||||
return this.navigate("", {
|
||||
trigger: true
|
||||
});
|
||||
this.navigate(allThreadsRoute, {trigger: true});
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.showNewPost = function() {
|
||||
var self = this;
|
||||
return $('.forum-content').fadeOut({
|
||||
$('.forum-content').fadeOut({
|
||||
duration: 200,
|
||||
complete: function() {
|
||||
return self.newPost.fadeIn(200).focus();
|
||||
@@ -151,7 +186,7 @@
|
||||
};
|
||||
|
||||
DiscussionRouter.prototype.hideNewPost = function() {
|
||||
return this.newPost.fadeOut({
|
||||
this.newPost.fadeOut({
|
||||
duration: 200,
|
||||
complete: function() {
|
||||
return $('.forum-content').fadeIn(200).find('.thread-wrapper').focus();
|
||||
|
||||
@@ -34,10 +34,11 @@
|
||||
course_settings: course_settings
|
||||
});
|
||||
/* jshint +W031*/
|
||||
return Backbone.history.start({
|
||||
pushState: true,
|
||||
root: "/courses/" + $$course_id + "/discussion/forum/"
|
||||
});
|
||||
if (!Backbone.History.started) {
|
||||
Backbone.history.start({pushState: true, root: "/courses/" + $$course_id + "/discussion/forum/"});
|
||||
} else {
|
||||
Backbone.history.loadUrl(window.location.pathname);
|
||||
}
|
||||
}
|
||||
};
|
||||
DiscussionProfileApp = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* globals $$course_id, Content, Markdown, URI */
|
||||
/* globals $$course_id, Content, Markdown, MathJax, URI */
|
||||
(function() {
|
||||
'use strict';
|
||||
this.DiscussionUtil = (function() {
|
||||
@@ -65,7 +65,7 @@
|
||||
DiscussionUtil.generateDiscussionLink = function(cls, txt, handler) {
|
||||
return $("<a>")
|
||||
.addClass("discussion-link").attr("href", "#")
|
||||
.addClass(cls).html(txt).click(function() {return handler(this);});
|
||||
.addClass(cls).text(txt).click(function() {return handler(this);});
|
||||
};
|
||||
|
||||
DiscussionUtil.urlFor = function(name, param, param1, param2) {
|
||||
@@ -134,15 +134,17 @@
|
||||
};
|
||||
|
||||
DiscussionUtil.showLoadingIndicator = function(element, takeFocus) {
|
||||
this.$_loading = $(
|
||||
"<div class='loading-animation' tabindex='0'><span class='sr'>" +
|
||||
gettext("Loading content") +
|
||||
"</span></div>"
|
||||
var animElem = edx.HtmlUtils.joinHtml(
|
||||
edx.HtmlUtils.HTML("<div class='loading-animation' tabindex='0'><span class='sr'>"),
|
||||
gettext("Loading content"),
|
||||
edx.HtmlUtils.HTML("</span></div>")
|
||||
);
|
||||
element.after(this.$_loading);
|
||||
var $animElem = $(animElem.toString());
|
||||
element.after($animElem);
|
||||
this.$_loading = $animElem;
|
||||
if (takeFocus) {
|
||||
this.makeFocusTrap(this.$_loading);
|
||||
return this.$_loading.focus();
|
||||
this.$_loading.focus();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -151,42 +153,35 @@
|
||||
};
|
||||
|
||||
DiscussionUtil.discussionAlert = function(header, body) {
|
||||
var alertDiv, alertTrigger;
|
||||
var $alertDiv, $alertTrigger;
|
||||
// Prevents "text" is undefined in underscore.js in tests - looks like some tests use
|
||||
// discussions somehow, but never append discussion fixtures or reset them; this causes
|
||||
// entire test suite (lms, cms, common) to fail due to unhandled JS exception
|
||||
var popupTemplate = $("#alert-popup").html() || "";
|
||||
if ($("#discussion-alert").length === 0) {
|
||||
alertDiv = $(
|
||||
"<div class='modal' role='alertdialog' id='discussion-alert' " +
|
||||
"aria-describedby='discussion-alert-message'/>"
|
||||
).css("display", "none");
|
||||
alertDiv.html(
|
||||
"<div class='inner-wrapper discussion-alert-wrapper'>" +
|
||||
" <button class='close-modal dismiss' title='" + gettext("Close") + "'>" +
|
||||
" <span class='icon fa fa-times' aria-hidden='true'></span>" +
|
||||
" </button>" +
|
||||
" <header><h2/><hr/></header>" +
|
||||
" <p id='discussion-alert-message'/><hr/>" +
|
||||
" <button class='dismiss'>" + gettext("OK") + "</button>" +
|
||||
"</div>"
|
||||
$alertDiv = $(
|
||||
edx.HtmlUtils.template(popupTemplate)({}).toString()
|
||||
);
|
||||
this.makeFocusTrap(alertDiv.find("button"));
|
||||
alertTrigger = $("<a href='#discussion-alert' id='discussion-alert-trigger'/>").css("display", "none");
|
||||
alertTrigger.leanModal({
|
||||
this.makeFocusTrap($alertDiv.find("button"));
|
||||
$alertTrigger = $("<a href='#discussion-alert' id='discussion-alert-trigger'/>").css("display", "none");
|
||||
$alertTrigger.leanModal({
|
||||
closeButton: "#discussion-alert .dismiss",
|
||||
overlay: 1,
|
||||
top: 200
|
||||
});
|
||||
$("body").append(alertDiv).append(alertTrigger);
|
||||
$("body").append($alertDiv).append($alertTrigger);
|
||||
}
|
||||
$("#discussion-alert header h2").html(header);
|
||||
$("#discussion-alert p").html(body);
|
||||
$("#discussion-alert header h2").text(header);
|
||||
$("#discussion-alert p").text(body);
|
||||
$("#discussion-alert-trigger").click();
|
||||
return $("#discussion-alert button").focus();
|
||||
$("#discussion-alert button").focus();
|
||||
};
|
||||
|
||||
DiscussionUtil.safeAjax = function(params) {
|
||||
var $elem, deferred, request,
|
||||
self = this;
|
||||
$elem = params.$elem;
|
||||
if ($elem && $elem.attr("disabled")) {
|
||||
if ($elem && $elem.prop("disabled")) {
|
||||
deferred = $.Deferred();
|
||||
deferred.reject();
|
||||
return deferred.promise();
|
||||
@@ -194,18 +189,6 @@
|
||||
params.url = URI(params.url).addSearch({
|
||||
ajax: 1
|
||||
});
|
||||
params.beforeSend = function() {
|
||||
if ($elem) {
|
||||
$elem.attr("disabled", "disabled");
|
||||
}
|
||||
if (params.$loading) {
|
||||
if (params.loadingCallback) {
|
||||
return params.loadingCallback.apply(params.$loading);
|
||||
} else {
|
||||
return self.showLoadingIndicator($(params.$loading), params.takeFocus);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!params.error) {
|
||||
params.error = function() {
|
||||
self.discussionAlert(
|
||||
@@ -216,9 +199,21 @@
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
if ($elem) {
|
||||
$elem.prop("disabled", true);
|
||||
}
|
||||
if (params.$loading) {
|
||||
if (params.loadingCallback) {
|
||||
params.loadingCallback.apply(params.$loading);
|
||||
} else {
|
||||
self.showLoadingIndicator(params.$loading, params.takeFocus);
|
||||
}
|
||||
}
|
||||
|
||||
request = $.ajax(params).always(function() {
|
||||
if ($elem) {
|
||||
$elem.removeAttr("disabled");
|
||||
$elem.prop("disabled", false);
|
||||
}
|
||||
if (params.$loading) {
|
||||
if (params.loadedCallback) {
|
||||
@@ -231,7 +226,7 @@
|
||||
return request;
|
||||
};
|
||||
|
||||
DiscussionUtil.updateWithUndo = function(model, updates, safeAjaxParams, errorMsg) {
|
||||
DiscussionUtil.updateWithUndo = function(model, updates, safeAjaxParams, errorMsg, beforeSend) {
|
||||
var undo,
|
||||
self = this;
|
||||
if (errorMsg) {
|
||||
@@ -241,6 +236,9 @@
|
||||
}
|
||||
undo = _.pick(model.attributes, _.keys(updates));
|
||||
model.set(updates);
|
||||
if (typeof beforeSend === 'function') {
|
||||
beforeSend();
|
||||
}
|
||||
return this.safeAjax(safeAjaxParams).fail(function() {
|
||||
return model.set(undo);
|
||||
});
|
||||
@@ -263,9 +261,12 @@
|
||||
|
||||
DiscussionUtil.formErrorHandler = function(errorsField) {
|
||||
return function(xhr, textStatus, error) {
|
||||
var makeErrorElem, response, _i, _len, _ref, _results;
|
||||
var makeErrorElem, response, _i, _len, _ref, _results, $errorItem;
|
||||
makeErrorElem = function(message) {
|
||||
return $("<li>").addClass("post-error").html(message);
|
||||
return edx.HtmlUtils.setHtml(
|
||||
$("<li>").addClass("post-error"),
|
||||
message
|
||||
);
|
||||
};
|
||||
errorsField.empty().show();
|
||||
if (xhr.status === 400) {
|
||||
@@ -275,14 +276,16 @@
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
error = _ref[_i];
|
||||
_results.push(errorsField.append(makeErrorElem(error)));
|
||||
$errorItem = makeErrorElem(error);
|
||||
_results.push(errorsField.append($errorItem));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
} else {
|
||||
return errorsField.append(makeErrorElem(
|
||||
gettext("We had some trouble processing your request. Please try again."))
|
||||
$errorItem = makeErrorElem(
|
||||
gettext("We had some trouble processing your request. Please try again.")
|
||||
);
|
||||
return errorsField.append($errorItem);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -291,11 +294,11 @@
|
||||
return errorsField.empty();
|
||||
};
|
||||
|
||||
DiscussionUtil.postMathJaxProcessor = function(text) {
|
||||
DiscussionUtil.postMathJaxProcessor = function(htmlSnippet) {
|
||||
var RE_DISPLAYMATH, RE_INLINEMATH;
|
||||
RE_INLINEMATH = /^\$([^\$]*)\$/g;
|
||||
RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g;
|
||||
return this.processEachMathAndCode(text, function(s, type) {
|
||||
return this.processEachMathAndCode(htmlSnippet, function(s, type) {
|
||||
if (type === 'display') {
|
||||
return s.replace(RE_DISPLAYMATH, function($0, $1) {
|
||||
return "\\[" + $1 + "\\]";
|
||||
@@ -353,84 +356,104 @@
|
||||
return this.getWmdEditor($content, $local, cls_identifier).refreshPreview();
|
||||
};
|
||||
|
||||
DiscussionUtil.processEachMathAndCode = function(text, processor) {
|
||||
var $div, ESCAPED_BACKSLASH, ESCAPED_DOLLAR, RE_DISPLAYMATH, RE_INLINEMATH, cnt, codeArchive, processedText;
|
||||
codeArchive = [];
|
||||
RE_DISPLAYMATH = /^([^\$]*?)\$\$([^\$]*?)\$\$(.*)$/m;
|
||||
RE_INLINEMATH = /^([^\$]*?)\$([^\$]+?)\$(.*)$/m;
|
||||
ESCAPED_DOLLAR = '@@ESCAPED_D@@';
|
||||
var RE_DISPLAYMATH = /^([^\$]*?)\$\$([^\$]*?)\$\$(.*)$/m,
|
||||
RE_INLINEMATH = /^([^\$]*?)\$([^\$]+?)\$(.*)$/m,
|
||||
ESCAPED_DOLLAR = '@@ESCAPED_D@@',
|
||||
ESCAPED_BACKSLASH = '@@ESCAPED_B@@';
|
||||
processedText = "";
|
||||
$div = $("<div>").html(text);
|
||||
|
||||
/**
|
||||
* Formats math and code chunks
|
||||
* @param htmlSnippet - post contents in form of safe (escaped and/or stripped) HTML
|
||||
* @param processor - callback to post-process math and code chunks. Should return HtmlUtils.HTML or "subclass"
|
||||
* @returns {*}
|
||||
*/
|
||||
DiscussionUtil.processEachMathAndCode = function(htmlSnippet, processor) {
|
||||
var $div, codeArchive, processedHtmlString, htmlString;
|
||||
codeArchive = {};
|
||||
processedHtmlString = "";
|
||||
$div = edx.HtmlUtils.setHtml($("<div>"), edx.HtmlUtils.ensureHtml(htmlSnippet));
|
||||
$div.find("code").each(function(index, code) {
|
||||
codeArchive.push($(code).html());
|
||||
return $(code).html(codeArchive.length - 1);
|
||||
codeArchive[index] = $(code).html();
|
||||
return $(code).text(index);
|
||||
});
|
||||
text = $div.html();
|
||||
text = text.replace(/\\\$/g, ESCAPED_DOLLAR);
|
||||
htmlString = $div.html();
|
||||
htmlString = htmlString.replace(/\\\$/g, ESCAPED_DOLLAR);
|
||||
// suppressing Don't make functions within a loop.
|
||||
/* jshint -W083 */
|
||||
while (true) {
|
||||
if (RE_INLINEMATH.test(text)) {
|
||||
text = text.replace(RE_INLINEMATH, function($0, $1, $2, $3) {
|
||||
processedText += $1 + processor("$" + $2 + "$", 'inline');
|
||||
if (RE_INLINEMATH.test(htmlString)) {
|
||||
htmlString = htmlString.replace(RE_INLINEMATH, function($0, $1, $2, $3) {
|
||||
processedHtmlString += $1 + processor("$" + $2 + "$", 'inline');
|
||||
return $3;
|
||||
});
|
||||
} else if (RE_DISPLAYMATH.test(text)) {
|
||||
text = text.replace(RE_DISPLAYMATH, function($0, $1, $2, $3) {
|
||||
} else if (RE_DISPLAYMATH.test(htmlString)) {
|
||||
htmlString = htmlString.replace(RE_DISPLAYMATH, function($0, $1, $2, $3) {
|
||||
/*
|
||||
bug fix, ordering is off
|
||||
*/
|
||||
processedText = processor("$$" + $2 + "$$", 'display') + processedText;
|
||||
processedText = $1 + processedText;
|
||||
processedHtmlString = processor("$$" + $2 + "$$", 'display') + processedHtmlString;
|
||||
processedHtmlString = $1 + processedHtmlString;
|
||||
return $3;
|
||||
});
|
||||
} else {
|
||||
processedText += text;
|
||||
processedHtmlString += htmlString;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* jshint +W083 */
|
||||
text = processedText;
|
||||
text = text.replace(new RegExp(ESCAPED_DOLLAR, 'g'), '\\$');
|
||||
text = text.replace(/\\\\\\\\/g, ESCAPED_BACKSLASH);
|
||||
text = text.replace(/\\begin\{([a-z]*\*?)\}([\s\S]*?)\\end\{\1\}/img, function($0, $1, $2) {
|
||||
htmlString = processedHtmlString;
|
||||
htmlString = htmlString.replace(new RegExp(ESCAPED_DOLLAR, 'g'), '\\$');
|
||||
htmlString = htmlString.replace(/\\\\\\\\/g, ESCAPED_BACKSLASH);
|
||||
htmlString = htmlString.replace(/\\begin\{([a-z]*\*?)\}([\s\S]*?)\\end\{\1\}/img, function($0, $1, $2) {
|
||||
return processor(("\\begin{" + $1 + "}") + $2 + ("\\end{" + $1 + "}"));
|
||||
});
|
||||
text = text.replace(new RegExp(ESCAPED_BACKSLASH, 'g'), '\\\\\\\\');
|
||||
$div = $("<div>").html(text);
|
||||
cnt = 0;
|
||||
htmlString = htmlString.replace(new RegExp(ESCAPED_BACKSLASH, 'g'), '\\\\\\\\');
|
||||
$div = edx.HtmlUtils.setHtml($("<div>"), edx.HtmlUtils.HTML(htmlString));
|
||||
$div.find("code").each(function(index, code) {
|
||||
$(code).html(processor(codeArchive[cnt], 'code'));
|
||||
return cnt += 1;
|
||||
edx.HtmlUtils.setHtml(
|
||||
$(code),
|
||||
edx.HtmlUtils.HTML(processor(codeArchive[index], 'code'))
|
||||
);
|
||||
});
|
||||
text = $div.html();
|
||||
return text;
|
||||
return edx.HtmlUtils.HTML($div.html());
|
||||
};
|
||||
|
||||
DiscussionUtil.unescapeHighlightTag = function(text) {
|
||||
return text.replace(
|
||||
/\<\;highlight\>\;/g,
|
||||
"<span class='search-highlight'>").replace(/\<\;\/highlight\>\;/g, "</span>"
|
||||
DiscussionUtil.unescapeHighlightTag = function(htmlSnippet) {
|
||||
return edx.HtmlUtils.HTML(
|
||||
htmlSnippet.toString().replace(
|
||||
/\<\;highlight\>\;/g,
|
||||
"<span class='search-highlight'>").replace(/\<\;\/highlight\>\;/g, "</span>"
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
DiscussionUtil.stripHighlight = function(text) {
|
||||
return text.replace(
|
||||
/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "").replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, ""
|
||||
);
|
||||
DiscussionUtil.stripHighlight = function(htmlString) {
|
||||
return htmlString
|
||||
.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "")
|
||||
.replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "");
|
||||
};
|
||||
|
||||
DiscussionUtil.stripLatexHighlight = function(text) {
|
||||
return this.processEachMathAndCode(text, this.stripHighlight);
|
||||
DiscussionUtil.stripLatexHighlight = function(htmlSnippet) {
|
||||
return this.processEachMathAndCode(htmlSnippet, this.stripHighlight);
|
||||
};
|
||||
|
||||
DiscussionUtil.markdownWithHighlight = function(text) {
|
||||
/**
|
||||
* Processes markdown into formatted text and handles highlighting.
|
||||
* @param unsafeText - raw markdown text, with all HTML entitites being *unescaped*.
|
||||
* @returns HtmlSnippet
|
||||
*/
|
||||
DiscussionUtil.markdownWithHighlight = function(unsafeText) {
|
||||
var converter;
|
||||
text = text.replace(/^\>\;/gm, ">");
|
||||
unsafeText = unsafeText.replace(/^\>\;/gm, ">");
|
||||
converter = Markdown.getMathCompatibleConverter();
|
||||
text = this.unescapeHighlightTag(this.stripLatexHighlight(converter.makeHtml(text)));
|
||||
return text.replace(/^>/gm, ">");
|
||||
/*
|
||||
* converter.makeHtml and HTML escaping:
|
||||
* - converter.makeHtml is not HtmlSnippet aware, so we must pass unescaped raw text
|
||||
* - converter.makeHtml strips html tags in post body and escapes in code blocks by design.
|
||||
* HTML tags are not supported. Only markdown is supported.
|
||||
*/
|
||||
var htmlSnippet = edx.HtmlUtils.HTML(converter.makeHtml(unsafeText));
|
||||
return this.unescapeHighlightTag(this.stripLatexHighlight(htmlSnippet));
|
||||
};
|
||||
|
||||
DiscussionUtil.abbreviateString = function(text, minLength) {
|
||||
@@ -444,19 +467,48 @@
|
||||
}
|
||||
};
|
||||
|
||||
DiscussionUtil.abbreviateHTML = function(html, minLength) {
|
||||
DiscussionUtil.convertMath = function(element) {
|
||||
edx.HtmlUtils.setHtml(
|
||||
element,
|
||||
this.postMathJaxProcessor(this.markdownWithHighlight(element.text()))
|
||||
);
|
||||
|
||||
this.typesetMathJax(element);
|
||||
};
|
||||
|
||||
DiscussionUtil.typesetMathJax = function(element) {
|
||||
if (typeof MathJax !== "undefined" && MathJax !== null) {
|
||||
MathJax.Hub.Queue(["Typeset", MathJax.Hub, element[0]]);
|
||||
}
|
||||
};
|
||||
|
||||
DiscussionUtil.abbreviateHTML = function(htmlSnippet, maxLength) {
|
||||
var $result, imagesToReplace, truncated_text;
|
||||
truncated_text = jQuery.truncate(html, {
|
||||
length: minLength,
|
||||
truncated_text = edx.HtmlUtils.HTML(jQuery.truncate(htmlSnippet.toString(), {
|
||||
length: maxLength,
|
||||
noBreaks: true,
|
||||
ellipsis: gettext('…')
|
||||
});
|
||||
$result = $("<div>" + truncated_text + "</div>");
|
||||
}));
|
||||
$result = $(edx.HtmlUtils.joinHtml(
|
||||
edx.HtmlUtils.HTML("<div>"),
|
||||
truncated_text,
|
||||
edx.HtmlUtils.HTML("</div>")
|
||||
).toString());
|
||||
imagesToReplace = $result.find("img:not(:first)");
|
||||
if (imagesToReplace.length > 0) {
|
||||
$result.append("<p><em>Some images in this post have been omitted</em></p>");
|
||||
edx.HtmlUtils.append(
|
||||
$result,
|
||||
edx.HtmlUtils.interpolateHtml(
|
||||
edx.HtmlUtils.HTML("<p><em>{text}</em></p>"),
|
||||
{text: gettext("Some images in this post have been omitted")}
|
||||
)
|
||||
);
|
||||
}
|
||||
imagesToReplace.replaceWith("<em>image omitted</em>");
|
||||
// See TNL-4983 for an explanation of why the linter requires ensureHtml()
|
||||
var afterMessage = edx.HtmlUtils.interpolateHtml(
|
||||
edx.HtmlUtils.HTML("<em>{text}</em>"), {text: gettext("image omitted")}
|
||||
);
|
||||
imagesToReplace.after(edx.HtmlUtils.ensureHtml(afterMessage).toString()).remove();
|
||||
return $result.html();
|
||||
};
|
||||
|
||||
|
||||
@@ -271,20 +271,22 @@
|
||||
return $button.toggleClass("is-checked", endorsed);
|
||||
},
|
||||
votes: function(votes) {
|
||||
var button, numVotes, selector, votesHtml, votesCountMsg;
|
||||
var button, numVotes, selector, votesText, votesCountMsg;
|
||||
selector = ".action-vote";
|
||||
this.updateButtonState(selector, window.user.voted(this.model));
|
||||
button = this.$el.find(selector);
|
||||
numVotes = votes.up_count;
|
||||
votesCountMsg = ngettext(
|
||||
"there is currently %(numVotes)s vote", "there are currently %(numVotes)s votes", numVotes
|
||||
"there is currently {numVotes} vote", "there are currently {numVotes} votes", numVotes
|
||||
);
|
||||
button.find(".js-sr-vote-count").html(interpolate(votesCountMsg, {numVotes: numVotes }, true));
|
||||
votesHtml = interpolate(ngettext("%(numVotes)s Vote", "%(numVotes)s Votes", numVotes), {
|
||||
numVotes: numVotes
|
||||
}, true);
|
||||
button.find(".vote-count").html(votesHtml);
|
||||
return this.$el.find('.display-vote .vote-count').html(votesHtml);
|
||||
button.find(".js-sr-vote-count").empty().text(
|
||||
edx.StringUtils.interpolate(votesCountMsg, {numVotes: numVotes })
|
||||
);
|
||||
votesText = edx.StringUtils.interpolate(
|
||||
ngettext("{numVotes} Vote", "{numVotes} Votes", numVotes),
|
||||
{ numVotes: numVotes });
|
||||
button.find(".vote-count").empty().text(votesText);
|
||||
this.$el.find('.display-vote .vote-count').empty().text(votesText);
|
||||
},
|
||||
pinned: function(pinned) {
|
||||
this.updateButtonState(".action-pin", pinned);
|
||||
@@ -389,18 +391,18 @@
|
||||
msg = gettext("We had some trouble removing this endorsement. Please try again.");
|
||||
}
|
||||
}
|
||||
beforeFunc = function() {
|
||||
return self.trigger("comment:endorse");
|
||||
};
|
||||
return DiscussionUtil.updateWithUndo(this.model, updates, {
|
||||
url: url,
|
||||
type: "POST",
|
||||
data: {
|
||||
endorsed: is_endorsing
|
||||
return DiscussionUtil.updateWithUndo(
|
||||
this.model,
|
||||
updates,
|
||||
{
|
||||
url: url,
|
||||
type: "POST",
|
||||
data: { endorsed: is_endorsing },
|
||||
$elem: $(event.currentTarget)
|
||||
},
|
||||
beforeSend: beforeFunc,
|
||||
$elem: $(event.currentTarget)
|
||||
}, msg).always(this.trigger("comment:endorse"));
|
||||
msg,
|
||||
function() { return self.trigger("comment:endorse"); }
|
||||
).always(this.trigger("comment:endorse"));
|
||||
};
|
||||
|
||||
DiscussionContentShowView.prototype.toggleVote = function(event) {
|
||||
|
||||
@@ -25,13 +25,14 @@
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var threadTypeTemplate,
|
||||
formId = _.uniqueId("form-");
|
||||
this.template = _.template($('#thread-edit-template').html());
|
||||
this.$el.html(this.template(this.model.toJSON())).appendTo(this.container);
|
||||
this.submitBtn = this.$('.post-update');
|
||||
threadTypeTemplate = _.template($("#thread-type-template").html());
|
||||
this.addField(threadTypeTemplate({form_id: formId}));
|
||||
var formId = _.uniqueId("form-"),
|
||||
threadTypeTemplate = edx.HtmlUtils.template($("#thread-type-template").html()),
|
||||
$threadTypeSelector = $(threadTypeTemplate({form_id: formId}).toString()),
|
||||
mainTemplate = edx.HtmlUtils.template($('#thread-edit-template').html());
|
||||
edx.HtmlUtils.setHtml(this.$el, mainTemplate(this.model.toJSON()));
|
||||
this.container.append(this.$el);
|
||||
this.$submitBtn = this.$('.post-update');
|
||||
this.addField($threadTypeSelector);
|
||||
this.$("#" + formId + "-post-type-" + this.threadType).attr('checked', true);
|
||||
// Only allow the topic field for course threads, as standalone threads
|
||||
// cannot be moved.
|
||||
@@ -46,8 +47,8 @@
|
||||
return this;
|
||||
},
|
||||
|
||||
addField: function(fieldView) {
|
||||
this.$('.forum-edit-post-form-wrapper').append(fieldView);
|
||||
addField: function($fieldView) {
|
||||
this.$('.forum-edit-post-form-wrapper').append($fieldView);
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -69,8 +70,8 @@
|
||||
}
|
||||
|
||||
return DiscussionUtil.safeAjax({
|
||||
$elem: this.submitBtn,
|
||||
$loading: this.submitBtn,
|
||||
$elem: this.$submitBtn,
|
||||
$loading: this.$submitBtn,
|
||||
url: DiscussionUtil.urlFor('update_thread', this.model.id),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
return self.displayedCollection.reset(discussion.models);
|
||||
});
|
||||
this.collection.on("add", this.addAndSelectThread);
|
||||
this.collection.on("thread:remove", this.threadRemoved);
|
||||
this.sidebar_padding = 10;
|
||||
this.boardName = null;
|
||||
this.template = _.template($("#thread-list-template").html());
|
||||
@@ -133,11 +134,12 @@
|
||||
});
|
||||
this.searchAlertCollection.on("add", function(searchAlert) {
|
||||
var content;
|
||||
content = _.template($("#search-alert-template").html())({
|
||||
'message': searchAlert.attributes.message,
|
||||
'cid': searchAlert.cid
|
||||
content = edx.HtmlUtils.template($("#search-alert-template").html())({
|
||||
'messageHtml': searchAlert.attributes.message,
|
||||
'cid': searchAlert.cid,
|
||||
'css_class': searchAlert.attributes.css_class
|
||||
});
|
||||
self.$(".search-alerts").append(content);
|
||||
edx.HtmlUtils.append(self.$(".search-alerts"), content);
|
||||
return self.$("#search-alert-" + searchAlert.cid + " a.dismiss")
|
||||
.bind("click", searchAlert, function(event) {
|
||||
return self.removeSearchAlert(event.data.cid);
|
||||
@@ -151,11 +153,19 @@
|
||||
});
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.addSearchAlert = function(message) {
|
||||
/**
|
||||
* Creates search alert model and adds it to collection
|
||||
* @param message - alert message
|
||||
* @param css_class - Allows setting custom css class for a message. This can be used to style messages
|
||||
* of different types differently (i.e. other background, completely hide, etc.)
|
||||
* @returns {Backbone.Model}
|
||||
*/
|
||||
DiscussionThreadListView.prototype.addSearchAlert = function(message, css_class) {
|
||||
var m;
|
||||
m = new Backbone.Model({
|
||||
"message": message
|
||||
});
|
||||
if (typeof css_class === 'undefined' || css_class === null) {
|
||||
css_class = "";
|
||||
}
|
||||
m = new Backbone.Model({"message": message, "css_class": css_class});
|
||||
this.searchAlertCollection.add(m);
|
||||
return m;
|
||||
};
|
||||
@@ -169,13 +179,13 @@
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.reloadDisplayedCollection = function(thread) {
|
||||
var active, content, current_el, thread_id;
|
||||
var active, $content, current_el, thread_id;
|
||||
this.clearSearchAlerts();
|
||||
thread_id = thread.get('id');
|
||||
content = this.renderThread(thread);
|
||||
$content = this.renderThread(thread);
|
||||
current_el = this.$(".forum-nav-thread[data-id=" + thread_id + "]");
|
||||
active = current_el.has(".forum-nav-thread-link.is-active").length !== 0;
|
||||
current_el.replaceWith(content);
|
||||
current_el.replaceWith($content);
|
||||
this.showMetadataAccordingToSort();
|
||||
if (active) {
|
||||
return this.setActiveThread(thread_id);
|
||||
@@ -236,12 +246,14 @@
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.render = function() {
|
||||
var self = this;
|
||||
var self = this,
|
||||
$elem = this.template({
|
||||
isCohorted: this.courseSettings.get("is_cohorted"),
|
||||
isPrivilegedUser: DiscussionUtil.isPrivilegedUser()
|
||||
});
|
||||
this.timer = 0;
|
||||
this.$el.html(this.template({
|
||||
isCohorted: this.courseSettings.get("is_cohorted"),
|
||||
isPrivilegedUser: DiscussionUtil.isPrivilegedUser()
|
||||
}));
|
||||
this.$el.empty();
|
||||
this.$el.append($elem);
|
||||
this.$(".forum-nav-sort-control option").removeProp("selected");
|
||||
this.$(".forum-nav-sort-control option[value=" + this.collection.sort_preference + "]")
|
||||
.prop("selected", true);
|
||||
@@ -258,20 +270,17 @@
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.renderThreads = function() {
|
||||
var content, rendered, thread, _i, _len, _ref;
|
||||
this.$(".forum-nav-thread-list").html("");
|
||||
rendered = $("<div></div>");
|
||||
_ref = this.displayedCollection.models;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
thread = _ref[_i];
|
||||
content = this.renderThread(thread);
|
||||
rendered.append(content);
|
||||
var $content, thread, i, len;
|
||||
this.$(".forum-nav-thread-list").empty();
|
||||
for (i = 0, len = this.displayedCollection.models.length; i < len; i++) {
|
||||
thread = this.displayedCollection.models[i];
|
||||
$content = this.renderThread(thread);
|
||||
this.$(".forum-nav-thread-list").append($content);
|
||||
}
|
||||
this.$(".forum-nav-thread-list").html(rendered.html());
|
||||
this.showMetadataAccordingToSort();
|
||||
this.renderMorePages();
|
||||
this.updateSidebar();
|
||||
return this.trigger("threads:rendered");
|
||||
this.trigger("threads:rendered");
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.showMetadataAccordingToSort = function() {
|
||||
@@ -291,19 +300,15 @@
|
||||
|
||||
DiscussionThreadListView.prototype.renderMorePages = function() {
|
||||
if (this.displayedCollection.hasMorePages()) {
|
||||
return this.$(".forum-nav-thread-list")
|
||||
.append(
|
||||
"<li class='forum-nav-load-more'>" +
|
||||
" <a href='#' class='forum-nav-load-more-link'>" + gettext("Load more") + "</a>" +
|
||||
"</li>"
|
||||
);
|
||||
edx.HtmlUtils.append(
|
||||
this.$(".forum-nav-thread-list"),
|
||||
edx.HtmlUtils.template($("#nav-load-more-link").html())({})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.getLoadingContent = function(srText) {
|
||||
return '<div class="forum-nav-loading" tabindex="0">' +
|
||||
' <span class="icon fa fa-spinner fa-spin"/><span class="sr" role="alert">' + srText + '</span>' +
|
||||
'</div>';
|
||||
return edx.HtmlUtils.template($("#nav-loading-template").html())({srText: srText});
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.loadMorePages = function(event) {
|
||||
@@ -313,7 +318,8 @@
|
||||
event.preventDefault();
|
||||
}
|
||||
loadMoreElem = this.$(".forum-nav-load-more");
|
||||
loadMoreElem.html(this.getLoadingContent(gettext("Loading more threads")));
|
||||
loadMoreElem.empty();
|
||||
edx.HtmlUtils.append(loadMoreElem, this.getLoadingContent(gettext("Loading more threads")));
|
||||
loadingElem = loadMoreElem.find(".forum-nav-loading");
|
||||
DiscussionUtil.makeFocusTrap(loadingElem);
|
||||
loadingElem.focus();
|
||||
@@ -374,8 +380,8 @@
|
||||
if (unreadCount > 0) {
|
||||
content.find('.forum-nav-thread-comments-count').attr(
|
||||
"data-tooltip",
|
||||
interpolate(
|
||||
ngettext('%(unread_count)s new comment', '%(unread_count)s new comments', unreadCount),
|
||||
edx.StringUtils.interpolate(
|
||||
ngettext('{unread_count} new comment', '{unread_count} new comments', unreadCount),
|
||||
{unread_count: unreadCount},
|
||||
true
|
||||
)
|
||||
@@ -392,23 +398,30 @@
|
||||
return false;
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.threadRemoved = function(thread_id) {
|
||||
return this.trigger("thread:removed", thread_id);
|
||||
DiscussionThreadListView.prototype.threadRemoved = function(thread) {
|
||||
this.trigger("thread:removed", thread);
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.setActiveThread = function(thread_id) {
|
||||
var $srElem;
|
||||
this.$(".forum-nav-thread-link").find(".sr").remove();
|
||||
this.$(".forum-nav-thread[data-id!='" + thread_id + "'] .forum-nav-thread-link")
|
||||
.removeClass("is-active");
|
||||
$srElem = edx.HtmlUtils.joinHtml(
|
||||
edx.HtmlUtils.HTML('<span class="sr">'),
|
||||
edx.HtmlUtils.ensureHtml(gettext("Current conversation")),
|
||||
edx.HtmlUtils.HTML('</span>')
|
||||
).toString();
|
||||
this.$(".forum-nav-thread[data-id='" + thread_id + "'] .forum-nav-thread-link")
|
||||
.addClass("is-active").find(".forum-nav-thread-wrapper-1")
|
||||
.prepend('<span class="sr">' + gettext("Current conversation") + '</span>');
|
||||
.prepend($srElem);
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.goHome = function() {
|
||||
var thread_id, url;
|
||||
var url, $tpl_content;
|
||||
this.template = _.template($("#discussion-home-template").html());
|
||||
$(".forum-content").html(this.template);
|
||||
$tpl_content = $(this.template());
|
||||
$(".forum-content").empty().append($tpl_content);
|
||||
$(".forum-nav-thread-list a").removeClass("is-active").find(".sr").remove();
|
||||
$("input.email-setting").bind("click", this.updateEmailNotifications);
|
||||
url = DiscussionUtil.urlFor("notifications_status", window.user.get("id"));
|
||||
@@ -416,19 +429,9 @@
|
||||
url: url,
|
||||
type: "GET",
|
||||
success: function(response) {
|
||||
if (response.status) {
|
||||
return $('input.email-setting').attr('checked', 'checked');
|
||||
} else {
|
||||
return $('input.email-setting').removeAttr('checked');
|
||||
}
|
||||
$('input.email-setting').prop('checked', response.status);
|
||||
}
|
||||
});
|
||||
thread_id = null;
|
||||
return this.trigger("thread:removed");
|
||||
/*
|
||||
select all threads
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.isBrowseMenuVisible = function() {
|
||||
@@ -505,19 +508,19 @@
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.getNameWidth = function(name) {
|
||||
var test, width;
|
||||
test = $("<div>");
|
||||
test.css({
|
||||
var $test, width;
|
||||
$test = $("<div>");
|
||||
$test.css({
|
||||
"font-size": this.$(".forum-nav-browse-current").css('font-size'),
|
||||
opacity: 0,
|
||||
position: 'absolute',
|
||||
left: -1000,
|
||||
top: -1000
|
||||
});
|
||||
$("body").append(test);
|
||||
test.html(name);
|
||||
width = test.width();
|
||||
test.remove();
|
||||
$("body").append($test);
|
||||
$test.text(name);
|
||||
width = $test.width();
|
||||
$test.remove();
|
||||
return width;
|
||||
};
|
||||
|
||||
@@ -653,8 +656,7 @@
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.searchFor = function(text) {
|
||||
var url,
|
||||
self = this;
|
||||
var url, self = this;
|
||||
this.clearSearchAlerts();
|
||||
this.clearFilters();
|
||||
this.mode = 'search';
|
||||
@@ -677,12 +679,16 @@
|
||||
dataType: 'json',
|
||||
$loading: $,
|
||||
loadingCallback: function() {
|
||||
return self.$(".forum-nav-thread-list")
|
||||
.html(
|
||||
"<li class='forum-nav-load-more'>" +
|
||||
self.getLoadingContent(gettext("Loading thread list")) +
|
||||
"</li>"
|
||||
);
|
||||
var element = self.$(".forum-nav-thread-list");
|
||||
element.empty();
|
||||
edx.HtmlUtils.append(
|
||||
element,
|
||||
edx.HtmlUtils.joinHtml(
|
||||
edx.HtmlUtils.HTML("<li class='forum-nav-load-more'>"),
|
||||
self.getLoadingContent(gettext("Loading thread list")),
|
||||
edx.HtmlUtils.HTML("</li>")
|
||||
)
|
||||
);
|
||||
},
|
||||
loadedCallback: function() {
|
||||
return self.$(".forum-nav-thread-list .forum-nav-load-more").remove();
|
||||
@@ -697,17 +703,22 @@
|
||||
if (!_.isNull(response.corrected_text)) {
|
||||
noResponseMsg = _.escape(
|
||||
gettext(
|
||||
'No results found for %(original_query)s. ' +
|
||||
'Showing results for %(suggested_query)s.'
|
||||
'No results found for {original_query}. ' +
|
||||
'Showing results for {suggested_query}.'
|
||||
)
|
||||
);
|
||||
message = interpolate(
|
||||
message = edx.HtmlUtils.interpolateHtml(
|
||||
noResponseMsg,
|
||||
{
|
||||
"original_query": "<em>" + _.escape(text) + "</em>",
|
||||
"suggested_query": "<em>" + response.corrected_text + "</em>"
|
||||
},
|
||||
true
|
||||
"original_query": edx.HtmlUtils.joinHtml(
|
||||
edx.HtmlUtils.HTML("<em>"), text, edx.HtmlUtils.HTML("</em>")
|
||||
),
|
||||
"suggested_query": edx.HtmlUtils.joinHtml(
|
||||
edx.HtmlUtils.HTML("<em>"),
|
||||
response.corrected_text ,
|
||||
edx.HtmlUtils.HTML("</em>")
|
||||
)
|
||||
}
|
||||
);
|
||||
self.addSearchAlert(message);
|
||||
} else if (response.discussion_data.length === 0) {
|
||||
@@ -731,18 +742,23 @@
|
||||
url: DiscussionUtil.urlFor("users"),
|
||||
type: "GET",
|
||||
dataType: 'json',
|
||||
error: function() {
|
||||
},
|
||||
error: function() {},
|
||||
success: function(response) {
|
||||
var message;
|
||||
var message, username;
|
||||
if (response.users.length > 0) {
|
||||
message = interpolate(_.escape(gettext('Show posts by %(username)s.')), {
|
||||
"username": _.template('<a class="link-jump" href="<%= url %>"><%- username %></a>')({
|
||||
url: DiscussionUtil.urlFor("user_profile", response.users[0].id),
|
||||
username: response.users[0].username
|
||||
})
|
||||
}, true);
|
||||
return self.addSearchAlert(message);
|
||||
username = edx.HtmlUtils.joinHtml(
|
||||
edx.HtmlUtils.interpolateHtml(
|
||||
edx.HtmlUtils.HTML('<a class="link-jump" href="{url}">'),
|
||||
{url: DiscussionUtil.urlFor("user_profile", response.users[0].id)}
|
||||
),
|
||||
response.users[0].username,
|
||||
edx.HtmlUtils.HTML("</a>")
|
||||
);
|
||||
|
||||
message = edx.HtmlUtils.interpolateHtml(
|
||||
gettext('Show posts by {username}.'), {"username": username}
|
||||
);
|
||||
return self.addSearchAlert(message, 'search-by-user');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -765,23 +781,17 @@
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.updateEmailNotifications = function() {
|
||||
if ($('input.email-setting').attr('checked')) {
|
||||
return DiscussionUtil.safeAjax({
|
||||
url: DiscussionUtil.urlFor("enable_notifications"),
|
||||
type: "POST",
|
||||
error: function() {
|
||||
return $('input.email-setting').removeAttr('checked');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return DiscussionUtil.safeAjax({
|
||||
url: DiscussionUtil.urlFor("disable_notifications"),
|
||||
type: "POST",
|
||||
error: function() {
|
||||
return $('input.email-setting').attr('checked', 'checked');
|
||||
}
|
||||
});
|
||||
}
|
||||
var $checkbox, checked, urlName;
|
||||
$checkbox = $('input.email-setting');
|
||||
checked = $checkbox.prop('checked');
|
||||
urlName = (checked) ? "enable_notifications" : "disable_notifications";
|
||||
DiscussionUtil.safeAjax({
|
||||
url: DiscussionUtil.urlFor(urlName),
|
||||
type: "POST",
|
||||
error: function() {
|
||||
$checkbox.prop('checked', !checked);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return DiscussionThreadListView;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
|
||||
DiscussionThreadProfileView.prototype.render = function() {
|
||||
var element, params;
|
||||
var params;
|
||||
this.convertMath();
|
||||
this.abbreviateBody();
|
||||
params = $.extend(this.model.toJSON(), {
|
||||
@@ -42,26 +42,24 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
this.$el.html(_.template($("#profile-thread-template").html())(params));
|
||||
edx.HtmlUtils.setHtml(
|
||||
this.$el,
|
||||
edx.HtmlUtils.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]]);
|
||||
}
|
||||
DiscussionUtil.typesetMathJax(this.$(".post-body"));
|
||||
return this;
|
||||
};
|
||||
|
||||
DiscussionThreadProfileView.prototype.convertMath = function() {
|
||||
return this.model.set(
|
||||
'markdownBody',
|
||||
DiscussionUtil.postMathJaxProcessor(DiscussionUtil.markdownWithHighlight(this.model.get('body')))
|
||||
);
|
||||
var htmlSnippet = DiscussionUtil.markdownWithHighlight(this.model.get('body'));
|
||||
this.model.set('markdownBody', htmlSnippet);
|
||||
};
|
||||
|
||||
DiscussionThreadProfileView.prototype.abbreviateBody = function() {
|
||||
var abbreviated;
|
||||
abbreviated = DiscussionUtil.abbreviateHTML(this.model.get('markdownBody'), 140);
|
||||
return this.model.set('abbreviatedBody', abbreviated);
|
||||
this.model.set('abbreviatedBody', abbreviated);
|
||||
};
|
||||
|
||||
return DiscussionThreadProfileView;
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
|
||||
DiscussionThreadShowView.prototype.renderTemplate = function() {
|
||||
var context;
|
||||
this.template = _.template($("#thread-show-template").html());
|
||||
context = $.extend({
|
||||
mode: this.mode,
|
||||
flagged: this.model.isFlagged(),
|
||||
@@ -46,27 +45,25 @@
|
||||
cid: this.model.cid,
|
||||
readOnly: $('.discussion-module').data('read-only')
|
||||
}, this.model.attributes);
|
||||
return this.template(context);
|
||||
return edx.HtmlUtils.template($("#thread-show-template").html())(context);
|
||||
};
|
||||
|
||||
DiscussionThreadShowView.prototype.render = function() {
|
||||
this.$el.html(this.renderTemplate());
|
||||
edx.HtmlUtils.setHtml(
|
||||
this.$el,
|
||||
this.renderTemplate()
|
||||
);
|
||||
this.delegateEvents();
|
||||
this.renderAttrs();
|
||||
this.$("span.timeago").timeago();
|
||||
this.convertMath();
|
||||
this.highlight(this.$(".post-body"));
|
||||
this.highlight(this.$("h1,h3"));
|
||||
this.$(".post-body");
|
||||
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]]);
|
||||
}
|
||||
DiscussionUtil.convertMath(this.$(".post-body"));
|
||||
};
|
||||
|
||||
DiscussionThreadShowView.prototype.edit = function(event) {
|
||||
@@ -77,12 +74,6 @@
|
||||
return this.trigger("thread:_delete", event);
|
||||
};
|
||||
|
||||
DiscussionThreadShowView.prototype.highlight = function(el) {
|
||||
if (el.html()) {
|
||||
return el.html(el.html().replace(/<mark>/g, "<mark>").replace(/<\/mark>/g, "</mark>"));
|
||||
}
|
||||
};
|
||||
|
||||
return DiscussionThreadShowView;
|
||||
|
||||
})(DiscussionContentShowView);
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
id = self.model.get("id");
|
||||
if (collection.get(id)) {
|
||||
self.model = collection.get(id);
|
||||
self.rerender();
|
||||
}
|
||||
});
|
||||
this.createShowView();
|
||||
@@ -133,7 +134,9 @@
|
||||
|
||||
DiscussionThreadView.prototype.render = function() {
|
||||
var self = this;
|
||||
this.$el.html(this.renderTemplate());
|
||||
var $element = $(this.renderTemplate());
|
||||
this.$el.empty();
|
||||
this.$el.append($element);
|
||||
this.delegateEvents();
|
||||
this.renderShowView();
|
||||
this.renderAttrs();
|
||||
@@ -215,7 +218,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
DiscussionThreadView.prototype.loadResponses = function(responseLimit, elem, firstLoad) {
|
||||
DiscussionThreadView.prototype.loadResponses = function(responseLimit, $elem, firstLoad) {
|
||||
var takeFocus,
|
||||
self = this;
|
||||
takeFocus = this.mode === "tab" ? false : true;
|
||||
@@ -227,8 +230,8 @@
|
||||
resp_skip: this.responses.size(),
|
||||
resp_limit: responseLimit ? responseLimit : void 0
|
||||
},
|
||||
$elem: elem,
|
||||
$loading: elem,
|
||||
$elem: $elem,
|
||||
$loading: $elem,
|
||||
takeFocus: takeFocus,
|
||||
complete: function() {
|
||||
self.responsesRequest = null;
|
||||
@@ -279,20 +282,20 @@
|
||||
};
|
||||
|
||||
DiscussionThreadView.prototype.renderResponseCountAndPagination = function(responseTotal) {
|
||||
var buttonText, loadMoreButton, responseCountFormat, responseLimit, responsePagination,
|
||||
var buttonText, $loadMoreButton, responseCountFormat, responseLimit, responsePagination,
|
||||
responsesRemaining, showingResponsesText, self = this;
|
||||
if (this.isQuestion() && this.markedAnswers.length !== 0) {
|
||||
responseCountFormat = ngettext(
|
||||
"%(numResponses)s other response", "%(numResponses)s other responses", responseTotal
|
||||
"{numResponses} other response", "{numResponses} other responses", responseTotal
|
||||
);
|
||||
} else {
|
||||
responseCountFormat = ngettext(
|
||||
"%(numResponses)s response", "%(numResponses)s responses", responseTotal
|
||||
"{numResponses} response", "{numResponses} responses", responseTotal
|
||||
);
|
||||
}
|
||||
this.$el.find(".response-count").html(interpolate(responseCountFormat, {
|
||||
numResponses: responseTotal
|
||||
}, true));
|
||||
this.$el.find(".response-count").text(
|
||||
edx.StringUtils.interpolate(responseCountFormat, {numResponses: responseTotal}, true)
|
||||
);
|
||||
responsePagination = this.$el.find(".response-pagination");
|
||||
responsePagination.empty();
|
||||
if (responseTotal > 0) {
|
||||
@@ -301,9 +304,9 @@
|
||||
showingResponsesText = gettext("Showing all responses");
|
||||
}
|
||||
else {
|
||||
showingResponsesText = interpolate(
|
||||
showingResponsesText = edx.StringUtils.interpolate(
|
||||
ngettext(
|
||||
"Showing first response", "Showing first %(numResponses)s responses",
|
||||
"Showing first response", "Showing first {numResponses} responses",
|
||||
this.responses.size()
|
||||
),
|
||||
{ numResponses: this.responses.size() },
|
||||
@@ -312,22 +315,22 @@
|
||||
}
|
||||
|
||||
responsePagination.append($("<span>")
|
||||
.addClass("response-display-count").html(_.escape(showingResponsesText)));
|
||||
.addClass("response-display-count").text(showingResponsesText));
|
||||
if (responsesRemaining > 0) {
|
||||
if (responsesRemaining < SUBSEQUENT_RESPONSE_PAGE_SIZE) {
|
||||
responseLimit = null;
|
||||
buttonText = gettext("Load all responses");
|
||||
} else {
|
||||
responseLimit = SUBSEQUENT_RESPONSE_PAGE_SIZE;
|
||||
buttonText = interpolate(gettext("Load next %(numResponses)s responses"), {
|
||||
buttonText = edx.StringUtils.interpolate(gettext("Load next {numResponses} responses"), {
|
||||
numResponses: responseLimit
|
||||
}, true);
|
||||
}
|
||||
loadMoreButton = $("<button>").addClass("load-response-button").html(_.escape(buttonText));
|
||||
loadMoreButton.click(function() {
|
||||
return self.loadResponses(responseLimit, loadMoreButton);
|
||||
$loadMoreButton = $("<button>").addClass("load-response-button").text(buttonText);
|
||||
$loadMoreButton.click(function() {
|
||||
return self.loadResponses(responseLimit, $loadMoreButton);
|
||||
});
|
||||
return responsePagination.append(loadMoreButton);
|
||||
return responsePagination.append($loadMoreButton);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,12 +37,14 @@
|
||||
ResponseCommentShowView.prototype.tagName = "li";
|
||||
|
||||
ResponseCommentShowView.prototype.render = function() {
|
||||
this.template = _.template($("#response-comment-show-template").html());
|
||||
this.$el.html(this.template(_.extend({
|
||||
var template = edx.HtmlUtils.template($("#response-comment-show-template").html());
|
||||
var context = _.extend({
|
||||
cid: this.model.cid,
|
||||
author_display: this.getAuthorDisplay(),
|
||||
readOnly: $('.discussion-module').data('read-only')
|
||||
}, this.model.attributes)));
|
||||
}, this.model.attributes);
|
||||
|
||||
edx.HtmlUtils.setHtml(this.$el, template(context));
|
||||
this.delegateEvents();
|
||||
this.renderAttrs();
|
||||
this.$el.find(".timeago").timeago();
|
||||
@@ -52,22 +54,25 @@
|
||||
};
|
||||
|
||||
ResponseCommentShowView.prototype.addReplyLink = function() {
|
||||
var html, name, p, _ref;
|
||||
var html, name;
|
||||
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);
|
||||
name = this.model.parent.get('username') || gettext("anonymous");
|
||||
html = edx.HtmlUtils.interpolateHtml(
|
||||
edx.HtmlUtils.HTML("<a href='#comment_{parent_id}'>@{name}</a>: "),
|
||||
{
|
||||
parent_id: this.model.parent.id,
|
||||
name: name
|
||||
}
|
||||
);
|
||||
return edx.HtmlUtils.prepend(
|
||||
this.$('.response-body p:first'),
|
||||
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]]);
|
||||
}
|
||||
DiscussionUtil.convertMath(this.$el.find(".response-body"));
|
||||
};
|
||||
|
||||
ResponseCommentShowView.prototype._delete = function(event) {
|
||||
|
||||
@@ -33,19 +33,18 @@
|
||||
};
|
||||
|
||||
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);
|
||||
var template = edx.HtmlUtils.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 template(context);
|
||||
};
|
||||
|
||||
ThreadResponseShowView.prototype.render = function() {
|
||||
this.$el.html(this.renderTemplate());
|
||||
edx.HtmlUtils.setHtml(this.$el, this.renderTemplate());
|
||||
this.delegateEvents();
|
||||
this.renderAttrs();
|
||||
this.$el.find(".posted-details .timeago").timeago();
|
||||
@@ -54,12 +53,7 @@
|
||||
};
|
||||
|
||||
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]]);
|
||||
}
|
||||
DiscussionUtil.convertMath(this.$(".response-body"));
|
||||
};
|
||||
|
||||
ThreadResponseShowView.prototype.edit = function(event) {
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
'use strict';
|
||||
describe('DiscussionUtil', function() {
|
||||
beforeEach(function() {
|
||||
return DiscussionSpecHelper.setUpGlobals();
|
||||
DiscussionSpecHelper.setUpGlobals();
|
||||
});
|
||||
return describe("updateWithUndo", function() {
|
||||
|
||||
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();
|
||||
@@ -45,13 +46,13 @@
|
||||
updates = {
|
||||
hello: "world"
|
||||
};
|
||||
$elem = jasmine.createSpyObj('$elem', ['attr']);
|
||||
$elem.attr.and.returnValue(true);
|
||||
$elem = jasmine.createSpyObj('$elem', ['prop']);
|
||||
$elem.prop.and.returnValue(true);
|
||||
res = DiscussionUtil.updateWithUndo(model, updates, {
|
||||
foo: "bar",
|
||||
$elem: $elem
|
||||
}, "error message");
|
||||
expect($elem.attr).toHaveBeenCalledWith("disabled");
|
||||
expect($elem.prop).toHaveBeenCalledWith("disabled");
|
||||
expect(DiscussionUtil.safeAjax).toHaveBeenCalled();
|
||||
expect(model.attributes).toEqual({
|
||||
hello: false,
|
||||
@@ -64,6 +65,30 @@
|
||||
return expect(failed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('safeAjax', function() {
|
||||
function dismissAlert() {
|
||||
$(".modal#discussion-alert").remove();
|
||||
}
|
||||
|
||||
it('respects global beforeSend', function() {
|
||||
var beforeSendSpy = jasmine.createSpy();
|
||||
$.ajaxSetup({beforeSend: beforeSendSpy});
|
||||
|
||||
var $elem = jasmine.createSpyObj('$elem', ['prop']);
|
||||
|
||||
DiscussionUtil.safeAjax({
|
||||
$elem: $elem,
|
||||
url: "/",
|
||||
type: "GET",
|
||||
dataType: "json"
|
||||
}).always(function() {
|
||||
dismissAlert();
|
||||
});
|
||||
expect($elem.prop).toHaveBeenCalledWith("disabled", true);
|
||||
expect(beforeSendSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
});
|
||||
});
|
||||
describe("search alerts", function() {
|
||||
var testAlertMessages;
|
||||
var testAlertMessages, getAlertMessagesAndClasses;
|
||||
|
||||
testAlertMessages = function(expectedMessages) {
|
||||
return expect($(".search-alert .message").map(function() {
|
||||
@@ -269,6 +269,15 @@
|
||||
}).get()).toEqual(expectedMessages);
|
||||
};
|
||||
|
||||
getAlertMessagesAndClasses = function() {
|
||||
return $(".search-alert").map(function() {
|
||||
return {
|
||||
text: $('.message', this).html(),
|
||||
css_class: $(this).attr('class')
|
||||
};
|
||||
}).get();
|
||||
};
|
||||
|
||||
it("renders and removes search alerts", function() {
|
||||
var bar, foo;
|
||||
testAlertMessages([]);
|
||||
@@ -282,6 +291,27 @@
|
||||
return testAlertMessages([]);
|
||||
});
|
||||
|
||||
it("renders search alert with custom class", function() {
|
||||
var foo, messages;
|
||||
testAlertMessages([]);
|
||||
|
||||
this.view.addSearchAlert("foo", "custom-class");
|
||||
messages = getAlertMessagesAndClasses();
|
||||
expect(messages.length).toEqual(1);
|
||||
expect(messages[0].text).toEqual("foo");
|
||||
expect(messages[0].css_class).toEqual("search-alert custom-class");
|
||||
|
||||
foo = this.view.addSearchAlert("bar", "other-class");
|
||||
|
||||
messages = getAlertMessagesAndClasses();
|
||||
expect(messages.length).toEqual(2);
|
||||
expect(messages[0].text).toEqual("foo");
|
||||
expect(messages[0].css_class).toEqual("search-alert custom-class");
|
||||
expect(messages[1].text).toEqual("bar");
|
||||
expect(messages[1].css_class).toEqual("search-alert other-class");
|
||||
});
|
||||
|
||||
|
||||
it("clears all search alerts", function() {
|
||||
this.view.addSearchAlert("foo");
|
||||
this.view.addSearchAlert("bar");
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
this.view.render();
|
||||
expectedGroupId = null;
|
||||
DiscussionSpecHelper.makeAjaxSpy(function(params) {
|
||||
return expect(params.data.group_id).toEqual(expectedGroupId);
|
||||
expect(params.data.group_id).toEqual(expectedGroupId);
|
||||
});
|
||||
return _.each(["1", "2", ""], function(groupIdStr) {
|
||||
expectedGroupId = groupIdStr;
|
||||
@@ -103,6 +103,7 @@
|
||||
self.view.$(".js-post-body textarea").val("dummy body");
|
||||
self.view.$(".forum-new-post-form").submit();
|
||||
expect($.ajax).toHaveBeenCalled();
|
||||
self.view.$(".forum-new-post-form").prop("disabled", false);
|
||||
return $.ajax.calls.reset();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,8 +33,10 @@
|
||||
}
|
||||
});
|
||||
this.event = DiscussionSpecHelper.makeEventSpy();
|
||||
this.event.target = $("body");
|
||||
spyOn(this.comment, "remove");
|
||||
return spyOn(this.view.$el, "remove");
|
||||
spyOn(this.view.$el, "remove");
|
||||
$(this.event.target).prop("disabled", false);
|
||||
});
|
||||
setAjaxResult = function(isSuccess) {
|
||||
return spyOn($, "ajax").and.callFake(function(params) {
|
||||
@@ -151,7 +153,7 @@
|
||||
this.view.$el.find(".edit-comment-body").html($("<textarea></textarea>"));
|
||||
this.view.$el.find(".edit-comment-body textarea").val(this.updatedBody);
|
||||
spyOn(this.view, 'cancelEdit');
|
||||
return spyOn($, "ajax").and.callFake(function(params) {
|
||||
spyOn($, "ajax").and.callFake(function(params) {
|
||||
if (self.ajaxSucceed) {
|
||||
params.success();
|
||||
} else {
|
||||
@@ -164,10 +166,17 @@
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
this.event = DiscussionSpecHelper.makeEventSpy();
|
||||
// All the way down in discussion/utils.js there's this line
|
||||
// element.after(...);
|
||||
// element is event.target in this case. This causes a JS exception, so we override the target
|
||||
this.event.target = $("body");
|
||||
$(this.event.target).prop("disabled", false);
|
||||
});
|
||||
it('calls the update endpoint correctly and displays the show view on success', function() {
|
||||
this.ajaxSucceed = true;
|
||||
this.view.update(DiscussionSpecHelper.makeEventSpy());
|
||||
this.view.update(this.event);
|
||||
expect($.ajax).toHaveBeenCalled();
|
||||
expect($.ajax.calls.mostRecent().args[0].url._parts.path)
|
||||
.toEqual('/courses/edX/999/test/discussion/comments/01234567/update');
|
||||
@@ -179,7 +188,7 @@
|
||||
var originalBody;
|
||||
originalBody = this.comment.get("body");
|
||||
this.ajaxSucceed = false;
|
||||
this.view.update(DiscussionSpecHelper.makeEventSpy());
|
||||
this.view.update(this.event);
|
||||
expect($.ajax).toHaveBeenCalled();
|
||||
expect($.ajax.calls.mostRecent().args[0].url._parts.path)
|
||||
.toEqual('/courses/edX/999/test/discussion/comments/01234567/update');
|
||||
|
||||
@@ -72,12 +72,12 @@
|
||||
'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'
|
||||
'user-profile', 'profile-thread', 'customwmd-prompt', 'nav-loading'
|
||||
];
|
||||
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'
|
||||
'forum-action-report', 'forum-action-pin', 'forum-action-close', 'forum-action-edit',
|
||||
'forum-action-delete', 'forum-actions', 'alert-popup', 'nav-load-more-link'
|
||||
];
|
||||
for (_i = 0, _len = templateNames.length; _i < _len; _i++) {
|
||||
templateName = templateNames[_i];
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class='modal' role='alertdialog' id='discussion-alert' aria-describedby='discussion-alert-message'>
|
||||
<div class='inner-wrapper discussion-alert-wrapper'>
|
||||
<button class='close-modal dismiss' title='<%- gettext("Close") %>''>
|
||||
<span class='icon fa fa-times' aria-hidden='true'></span>
|
||||
</button>
|
||||
<header>
|
||||
<h2/></h2>
|
||||
<hr/>
|
||||
</header>
|
||||
<p id='discussion-alert-message'></p>
|
||||
<hr/>
|
||||
<button class='dismiss'><%- gettext("OK") %></button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<li class='forum-nav-load-more'>
|
||||
<a href='#' class='forum-nav-load-more-link'>
|
||||
<%- gettext("Load more") %>
|
||||
</a>
|
||||
</li>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="forum-nav-loading" tabindex="0">
|
||||
<span class="icon fa fa-spinner fa-spin"/>
|
||||
<span class="sr" role="alert"><%- srText %></span>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="search-alert" id="search-alert-<%- cid %>">
|
||||
<div class="search-alert <%- css_class %>" id="search-alert-<%- cid %>">
|
||||
<div class="search-alert-content">
|
||||
<p class="message"><%= message %></p>
|
||||
<p class="message"><%= HtmlUtils.ensureHtml(messageHtml) %></p>
|
||||
</div>
|
||||
|
||||
<div class="search-alert-controls">
|
||||
|
||||
@@ -35,6 +35,8 @@ var options = {
|
||||
{pattern: 'common/js/vendor/backbone.js', included: true},
|
||||
|
||||
{pattern: 'edx-ui-toolkit/js/utils/global-loader.js', included: true},
|
||||
{pattern: 'edx-ui-toolkit/js/utils/string-utils.js', included: true},
|
||||
{pattern: 'edx-ui-toolkit/js/utils/html-utils.js', included: true},
|
||||
|
||||
{pattern: 'js/vendor/jasmine-imagediff.js', included: true},
|
||||
{pattern: 'common/js/spec_helpers/jasmine-extensions.js', included: true},
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
<%! import json %>
|
||||
<%! from openedx.core.djangolib.js_utils import js_escaped_string %>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.PLATFORM_NAME = ${json.dumps(settings.PLATFORM_NAME)};
|
||||
window.ENABLE_DISCUSSION_HOME_PANEL = ${json.dumps(settings.FEATURES.get('ENABLE_DISCUSSION_HOME_PANEL', False))};
|
||||
window.PLATFORM_NAME = "${settings.PLATFORM_NAME | n, js_escaped_string}";
|
||||
% if settings.FEATURES.get('ENABLE_DISCUSSION_HOME_PANEL', False):
|
||||
window.ENABLE_DISCUSSION_HOME_PANEL = true;
|
||||
% else:
|
||||
window.ENABLE_DISCUSSION_HOME_PANEL = false;
|
||||
% endif
|
||||
</script>
|
||||
|
||||
<%
|
||||
@@ -11,7 +16,14 @@ template_names = [
|
||||
'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', 'customwmd-prompt'
|
||||
'inline-discussion', 'pagination', 'user-profile', 'profile-thread', 'customwmd-prompt', 'nav-loading'
|
||||
]
|
||||
|
||||
## same, but without trailing "-template" in script ID - these templates does not contain any free variables
|
||||
template_names_no_suffix = [
|
||||
'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',
|
||||
'alert-popup', 'nav-load-more-link'
|
||||
]
|
||||
%>
|
||||
|
||||
@@ -21,8 +33,8 @@ template_names = [
|
||||
</script>
|
||||
% endfor
|
||||
|
||||
## same, but without trailing "-template" in script ID
|
||||
% for template_name in ['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 template_name in template_names_no_suffix:
|
||||
<script aria-hidden="true" type="text/template" id="${template_name}">
|
||||
<%static:include path="common/templates/discussion/${template_name}.underscore" />
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<%inherit file="../main.html" />
|
||||
<%page expression_filter="h"/>
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
<%!
|
||||
from django.utils.translation import ugettext as _
|
||||
@@ -6,7 +7,7 @@ from django.template.defaultfilters import escapejs
|
||||
%>
|
||||
|
||||
<%block name="bodyclass">discussion</%block>
|
||||
<%block name="pagetitle">${_("Discussion - {course_number}").format(course_number=course.display_number_with_default) | h}</%block>
|
||||
<%block name="pagetitle">${_("Discussion - {course_number}").format(course_number=course.display_number_with_default)}</%block>
|
||||
|
||||
<%block name="headextra">
|
||||
<%static:css group='style-course-vendor'/>
|
||||
@@ -33,7 +34,7 @@ from django.template.defaultfilters import escapejs
|
||||
|
||||
</nav>
|
||||
</section>
|
||||
<section class="course-content container discussion-user-threads" data-course-id="${course.id | h}" data-course-name="${course.display_name_with_default_escaped | h}" data-threads="${threads | h}" data-user-info="${user_info | h}" data-page="${page | h}" data-num-pages="${num_pages | h}"/>
|
||||
<section class="course-content container discussion-user-threads" data-course-id="${course.id}" data-course-name="${course.display_name_with_default}" data-threads="${threads}" data-user-info="${user_info}" data-page="${page}" data-num-pages="${num_pages}"/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user