Commit 2d67a4c0 by David Ormsbee

Merge branch 'master' into feature/rocky/discussion

parents 0e21ed03 daf33dbc
...@@ -229,6 +229,10 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -229,6 +229,10 @@ class CourseDescriptor(SequenceDescriptor):
return self.metadata.get('css_class', '') return self.metadata.get('css_class', '')
@property @property
def info_sidebar_name(self):
return self.metadata.get('info_sidebar_name', 'Course Handouts')
@property
def title(self): def title(self):
return self.display_name return self.display_name
......
...@@ -152,7 +152,7 @@ CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { ...@@ -152,7 +152,7 @@ CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) {
function blockNormal(stream, state) { function blockNormal(stream, state) {
var match; var match;
if (stream.match(circuitRE)) { if (stream.sol() && stream.match(circuitRE)) {
stream.skipToEnd(); stream.skipToEnd();
return circuit_formatter; return circuit_formatter;
} else if (state.indentationDiff >= 4) { } else if (state.indentationDiff >= 4) {
......
...@@ -324,7 +324,10 @@ WIKI_ACCOUNT_HANDLING = False ...@@ -324,7 +324,10 @@ WIKI_ACCOUNT_HANDLING = False
WIKI_EDITOR = 'course_wiki.editors.CodeMirror' WIKI_EDITOR = 'course_wiki.editors.CodeMirror'
WIKI_SHOW_MAX_CHILDREN = 0 # We don't use the little menu that shows children of an article in the breadcrumb WIKI_SHOW_MAX_CHILDREN = 0 # We don't use the little menu that shows children of an article in the breadcrumb
WIKI_ANONYMOUS = False # Don't allow anonymous access until the styling is figured out WIKI_ANONYMOUS = False # Don't allow anonymous access until the styling is figured out
WIKI_CAN_CHANGE_PERMISSIONS = lambda article, user: user.has_perm('wiki.assign') WIKI_CAN_CHANGE_PERMISSIONS = lambda article, user: user.is_staff or user.is_superuser
WIKI_CAN_ASSIGN = lambda article, user: user.is_staff or user.is_superuser
WIKI_USE_BOOTSTRAP_SELECT_WIDGET = False
################################# Jasmine ################################### ################################# Jasmine ###################################
JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee' JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee'
...@@ -417,6 +420,8 @@ main_vendor_js = [ ...@@ -417,6 +420,8 @@ main_vendor_js = [
'js/vendor/jquery.qtip.min.js', 'js/vendor/jquery.qtip.min.js',
] ]
discussion_js = glob2.glob(PROJECT_ROOT / 'static/coffee/src/discussion/*.coffee')
# Load javascript from all of the available xmodules, and # Load javascript from all of the available xmodules, and
# prep it for use in pipeline js # prep it for use in pipeline js
from xmodule.x_module import XModuleDescriptor from xmodule.x_module import XModuleDescriptor
...@@ -487,7 +492,7 @@ PIPELINE_JS = { ...@@ -487,7 +492,7 @@ PIPELINE_JS = {
] + [ ] + [
pth.replace(PROJECT_ROOT / 'static/', '') pth.replace(PROJECT_ROOT / 'static/', '')
for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/src/**/*.coffee')\ for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/src/**/*.coffee')\
if pth not in courseware_only_js if pth not in courseware_only_js and pth not in discussion_js
] + [ ] + [
'js/form.ext.js', 'js/form.ext.js',
'js/my_courses_dropdown.js', 'js/my_courses_dropdown.js',
...@@ -511,6 +516,10 @@ PIPELINE_JS = { ...@@ -511,6 +516,10 @@ PIPELINE_JS = {
'spec': { 'spec': {
'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/spec/**/*.coffee')], 'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/spec/**/*.coffee')],
'output_filename': 'js/lms-spec.js' 'output_filename': 'js/lms-spec.js'
},
'discussion' : {
'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in discussion_js],
'output_filename': 'js/discussion.js'
} }
} }
......
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
initializeFollowDiscussion = (discussion) ->
$discussion = $(discussion)
id = $following.attr("_id")
$local = Discussion.generateLocal()
$discussion.children(".discussion-non-content")
.find(".discussion-title-wrapper")
.append(Discussion.subscriptionLink('discussion', id))
@Discussion = $.extend @Discussion,
initializeDiscussion: (discussion) ->
$discussion = $(discussion)
$discussion.find(".thread").each (index, thread) ->
Discussion.initializeContent(thread)
Discussion.bindContentEvents(thread)
$discussion.find(".comment").each (index, comment) ->
Discussion.initializeContent(comment)
Discussion.bindContentEvents(comment)
#initializeFollowDiscussion(discussion) TODO move this somewhere else
bindDiscussionEvents: (discussion) ->
$discussion = $(discussion)
$discussionNonContent = $discussion.children(".discussion-non-content")
$local = Discussion.generateLocal($discussion.children(".discussion-local"))
id = $discussion.attr("_id")
handleSubmitNewPost = (elem) ->
title = $local(".new-post-title").val()
body = Discussion.getWmdContent $discussion, $local, "new-post-body"
tags = $local(".new-post-tags").val()
url = Discussion.urlFor('create_thread', id)
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: 'json'
data:
title: title
body: body
tags: tags
error: Discussion.formErrorHandler($local(".new-post-form-errors"))
success: (response, textStatus) ->
Discussion.clearFormErrors($local(".new-post-form-errors"))
$thread = $(response.html)
$discussion.children(".threads").prepend($thread)
$local(".new-post-title").val("")
Discussion.setWmdContent $discussion, $local, "new-post-body", ""
$local(".new-post-tags").val("")
if $discussion.hasClass("inline-discussion")
$local(".new-post-form").addClass("collapsed")
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").hide()
handleCancelNewPost = (elem) ->
if $discussion.hasClass("inline-discussion")
$local(".new-post-form").addClass("collapsed")
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").hide()
handleSimilarPost = (elem) ->
$title = $local(".new-post-title")
$wrapper = $local(".new-post-similar-posts-wrapper")
$similarPosts = $local(".new-post-similar-posts")
prevText = $title.attr("prev-text")
text = $title.val()
if text == prevText
if $local(".similar-post").length
$wrapper.show()
else if $.trim(text).length
Discussion.safeAjax
$elem: $(elem)
url: Discussion.urlFor 'search_similar_threads', id
type: "GET"
dateType: 'json'
data:
text: $local(".new-post-title").val()
success: (response, textStatus) ->
$similarPosts.empty()
console.log response
if $.type(response) == "array" and response.length
$wrapper.show()
for thread in response
#singleThreadUrl = Discussion.urlFor 'retrieve_single_thread
$similarPost = $("<a>").addClass("similar-post")
.html(thread["title"])
.attr("href", "javascript:void(0)") #TODO
.appendTo($similarPosts)
else
$wrapper.hide()
else
$wrapper.hide()
$title.attr("prev-text", text)
initializeNewPost = ->
view = { discussion_id: id }
$discussionNonContent = $discussion.children(".discussion-non-content")
if not $local(".wmd-panel").length
$discussionNonContent.append Mustache.render Discussion.newPostTemplate, view
$newPostBody = $local(".new-post-body")
Discussion.makeWmdEditor $discussion, $local, "new-post-body"
$input = Discussion.getWmdInput($discussion, $local, "new-post-body")
$input.attr("placeholder", "post a new topic...")
if $discussion.hasClass("inline-discussion")
$input.bind 'focus', (e) ->
$local(".new-post-form").removeClass('collapsed')
else if $discussion.hasClass("forum-discussion")
$local(".new-post-form").removeClass('collapsed')
$local(".new-post-tags").tagsInput Discussion.tagsInputOptions()
$local(".new-post-title").blur ->
handleSimilarPost(this)
$local(".hide-similar-posts").click ->
$local(".new-post-similar-posts-wrapper").hide()
$local(".discussion-submit-post").click ->
handleSubmitNewPost(this)
$local(".discussion-cancel-post").click ->
handleCancelNewPost(this)
$local(".new-post-form").show()
handleAjaxReloadDiscussion = (elem, url) ->
if not url then return
$elem = $(elem)
$discussion = $elem.parents("section.discussion")
Discussion.safeAjax
$elem: $elem
url: url
type: "GET"
dataType: 'html'
success: (data, textStatus) ->
$data = $(data)
$parent = $discussion.parent()
$discussion.replaceWith($data)
$discussion = $parent.children(".discussion")
Discussion.initializeDiscussion($discussion)
Discussion.bindDiscussionEvents($discussion)
handleAjaxSearch = (elem) ->
$elem = $(elem)
url = URI($elem.attr("action")).addSearch({text: $local(".search-input").val()})
handleAjaxReloadDiscussion($elem, url)
handleAjaxSort = (elem) ->
$elem = $(elem)
url = $elem.attr("sort-url")
handleAjaxReloadDiscussion($elem, url)
handleAjaxPage = (elem) ->
$elem = $(elem)
url = $elem.attr("page-url")
handleAjaxReloadDiscussion($elem, url)
if $discussion.hasClass("inline-discussion")
initializeNewPost()
if $discussion.hasClass("forum-discussion")
$discussionSidebar = $(".discussion-sidebar")
if $discussionSidebar.length
$sidebarLocal = Discussion.generateLocal($discussionSidebar)
Discussion.bindLocalEvents $sidebarLocal,
"click .sidebar-new-post-button": (event) ->
initializeNewPost()
Discussion.bindLocalEvents $local,
"submit .search-wrapper>.discussion-search-form": (event) ->
event.preventDefault()
handleAjaxSearch(this)
"click .discussion-search-link": ->
handleAjaxSearch($local(".search-wrapper>.discussion-search-form"))
"click .discussion-sort-link": ->
handleAjaxSort(this)
$discussion.children(".discussion-paginator").find(".discussion-page-link").unbind('click').click ->
handleAjaxPage(this)
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
@Discussion = $.extend @Discussion,
initializeDiscussionModule: (elem) ->
$discussionModule = $(elem)
$local = Discussion.generateLocal($discussionModule)
handleShowDiscussion = (elem) ->
$elem = $(elem)
if not $local("section.discussion").length
discussion_id = $elem.attr("discussion_id")
url = Discussion.urlFor 'retrieve_discussion', discussion_id
Discussion.safeAjax
$elem: $elem
url: url
type: "GET"
success: (data, textStatus, xhr) ->
$discussionModule.append(data)
discussion = $local("section.discussion")
Discussion.initializeDiscussion(discussion)
Discussion.bindDiscussionEvents(discussion)
$elem.html("Hide Discussion")
$elem.unbind('click').click ->
handleHideDiscussion(this)
dataType: 'html'
else
$local("section.discussion").show()
$elem.html("Hide Discussion")
$elem.unbind('click').click ->
handleHideDiscussion(this)
handleHideDiscussion = (elem) ->
$local("section.discussion").hide()
$elem = $(elem)
$elem.html("Show Discussion")
$elem.unbind('click').click ->
handleShowDiscussion(this)
$local(".discussion-show").click ->
handleShowDiscussion(this)
$ ->
#toggle = ->
# $('.course-wrapper').toggleClass('closed')
#Discussion = window.Discussion
#if $('#accordion').length
# active = $('#accordion ul:has(li.active)').index('#accordion ul')
# $('#accordion').bind('accordionchange', @log).accordion
# active: if active >= 0 then active else 1
# header: 'h3'
# autoHeight: false
# $('#open_close_accordion a').click toggle
# $('#accordion').show()
#$(".discussion-module").each (index, elem) ->
# Discussion.initializeDiscussionModule(elem)
#$("section.discussion").each (index, discussion) ->
# Discussion.initializeDiscussion(discussion)
# Discussion.bindDiscussionEvents(discussion)
#Discussion.initializeUserProfile($(".discussion-sidebar>.user-profile"))
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
@Discussion = $.extend @Discussion,
newPostTemplate: """
<form class="new-post-form collapsed" id="new-post-form" style="display: block; ">
<ul class="new-post-form-errors discussion-errors"></ul>
<input type="text" class="new-post-title title-input" placeholder="Title" />
<div class="new-post-similar-posts-wrapper" style="display: none">
Similar Posts:
<a class="hide-similar-posts" href="javascript:void(0)">Hide</a>
<div class="new-post-similar-posts"></div>
</div>
<div class="new-post-body reply-body"></div>
<input class="new-post-tags" placeholder="Tags" />
<div class="post-options">
<input type="checkbox" class="discussion-post-anonymously" id="discussion-post-anonymously-${discussion_id}">
<label for="discussion-post-anonymously-${discussion_id}">post anonymously</label>
<input type="checkbox" class="discussion-auto-watch" id="discussion-autowatch-${discussion_id}" checked="">
<label for="discussion-auto-watch-${discussion_id}">follow this thread</label>
</div>
<div class="new-post-control post-control">
<a class="discussion-cancel-post" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-post control-button" href="javascript:void(0)">Submit</a>
</div>
</form>
"""
replyTemplate: """
<form class="discussion-reply-new">
<ul class="discussion-errors"></ul>
<div class="reply-body"></div>
<input type="checkbox" class="discussion-post-anonymously" id="discussion-post-anonymously-{{id}}" />
<label for="discussion-post-anonymously-{{id}}">post anonymously</label>
{{#showWatchCheckbox}}
<input type="checkbox" class="discussion-auto-watch" id="discussion-autowatch-{{id}}" checked />
<label for="discussion-auto-watch-{{id}}">follow this thread</label>
{{/showWatchCheckbox}}
<br />
<div class = "reply-post-control">
<a class="discussion-cancel-post" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-post control-button" href="javascript:void(0)">Submit</a>
</div>
</form>
"""
editThreadTemplate: """
<form class="discussion-content-edit discussion-thread-edit" _id="{{id}}">
<ul class="discussion-errors discussion-update-errors"></ul>
<input type="text" class="thread-title-edit title-input" placeholder="Title" value="{{title}}"/>
<div class="thread-body-edit body-input">{{body}}</div>
<input class="thread-tags-edit" placeholder="Tags" value="{{tags}}" />
<div class = "edit-post-control">
<a class="discussion-cancel-update" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-update control-button" href="javascript:void(0)">Update</a>
</div>
</form>
"""
editCommentTemplate: """
<form class="discussion-content-edit discussion-comment-edit" _id="{{id}}">
<ul class="discussion-errors discussion-update-errors"></ul>
<div class="comment-body-edit body-input">{{body}}</div>
<div class = "edit-post-control">
<a class="discussion-cancel-update" href="javascript:void(0)">Cancel</a>
<a class="discussion-submit-update control-button" href="javascript:void(0)">Update</a>
</div>
</form>
"""
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
@Discussion = $.extend @Discussion,
initializeUserProfile: ($userProfile) ->
$local = Discussion.generateLocal $userProfile
handleUpdateModeratorStatus = (elem, isModerator) ->
confirmValue = confirm("Are you sure?")
if not confirmValue then return
url = Discussion.urlFor('update_moderator_status', $$profiled_user_id)
Discussion.safeAjax
$elem: $(elem)
url: url
type: "POST"
dataType: 'json'
data:
is_moderator: isModerator
error: (response, textStatus, e) ->
console.log e
success: (response, textStatus) ->
parent = $userProfile.parent()
$userProfile.replaceWith(response.html)
Discussion.initializeUserProfile parent.children(".user-profile")
Discussion.bindLocalEvents $local,
"click .sidebar-revoke-moderator-button": (event) ->
handleUpdateModeratorStatus(this, false)
"click .sidebar-promote-moderator-button": (event) ->
handleUpdateModeratorStatus(this, true)
initializeUserActiveDiscussion: ($discussion) ->
if not @Discussion?
@Discussion = {}
Discussion = @Discussion
wmdEditors = {}
@Discussion = $.extend @Discussion,
generateLocal: (elem) ->
(selector) -> $(elem).find(selector)
generateDiscussionLink: (cls, txt, handler) ->
$("<a>").addClass("discussion-link")
.attr("href", "javascript:void(0)")
.addClass(cls).html(txt)
.click -> handler(this)
urlFor: (name, param, param1, param2) ->
{
follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow"
unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow"
create_thread : "/courses/#{$$course_id}/discussion/#{param}/threads/create"
search_similar_threads : "/courses/#{$$course_id}/discussion/#{param}/threads/search_similar"
update_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/update"
create_comment : "/courses/#{$$course_id}/discussion/threads/#{param}/reply"
delete_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/delete"
upvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/upvote"
downvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/downvote"
undo_vote_for_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unvote"
follow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/follow"
unfollow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unfollow"
update_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/update"
endorse_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/endorse"
create_sub_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/reply"
delete_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/delete"
upvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/upvote"
downvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/downvote"
undo_vote_for_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/unvote"
upload : "/courses/#{$$course_id}/discussion/upload"
search : "/courses/#{$$course_id}/discussion/forum/search"
tags_autocomplete : "/courses/#{$$course_id}/discussion/threads/tags/autocomplete"
retrieve_discussion : "/courses/#{$$course_id}/discussion/forum/#{param}/inline"
retrieve_single_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}"
update_moderator_status : "/courses/#{$$course_id}/discussion/users/#{param}/update_moderator_status"
openclose_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/close"
permanent_link_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}"
permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}"
}[name]
safeAjax: (params) ->
$elem = params.$elem
if $elem.attr("disabled")
return
$elem.attr("disabled", "disabled")
$.ajax(params).always ->
$elem.removeAttr("disabled")
handleAnchorAndReload: (response) ->
#window.location = window.location.pathname + "#" + response['id']
window.location.reload()
bindLocalEvents: ($local, eventsHandler) ->
for eventSelector, handler of eventsHandler
[event, selector] = eventSelector.split(' ')
$local(selector).unbind(event)[event] handler
tagsInputOptions: ->
autocomplete_url: Discussion.urlFor('tags_autocomplete')
autocomplete:
remoteDataType: 'json'
interactive: true
height: '30px'
width: '100%'
defaultText: "Tag your post: press enter after each tag"
removeWithBackspace: true
isSubscribed: (id, type) ->
$$user_info? and (
if type == "thread"
id in $$user_info.subscribed_thread_ids
else if type == "commentable" or type == "discussion"
id in $$user_info.subscribed_commentable_ids
else
id in $$user_info.subscribed_user_ids
)
isUpvoted: (id) ->
$$user_info? and (id in $$user_info.upvoted_ids)
isDownvoted: (id) ->
$$user_info? and (id in $$user_info.downvoted_ids)
formErrorHandler: (errorsField) ->
(xhr, textStatus, error) ->
response = JSON.parse(xhr.responseText)
if response.errors? and response.errors.length > 0
errorsField.empty()
for error in response.errors
errorsField.append($("<li>").addClass("new-post-form-error").html(error))
clearFormErrors: (errorsField) ->
errorsField.empty()
postMathJaxProcessor: (text) ->
RE_INLINEMATH = /^\$([^\$]*)\$/g
RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g
Discussion.processEachMathAndCode text, (s, type) ->
if type == 'display'
s.replace RE_DISPLAYMATH, ($0, $1) ->
"\\[" + $1 + "\\]"
else if type == 'inline'
s.replace RE_INLINEMATH, ($0, $1) ->
"\\(" + $1 + "\\)"
else
s
makeWmdEditor: ($content, $local, cls_identifier) ->
elem = $local(".#{cls_identifier}")
id = $content.attr("_id")
appended_id = "-#{cls_identifier}-#{id}"
imageUploadUrl = Discussion.urlFor('upload')
editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, Discussion.postMathJaxProcessor
wmdEditors["#{cls_identifier}-#{id}"] = editor
editor
getWmdEditor: ($content, $local, cls_identifier) ->
id = $content.attr("_id")
wmdEditors["#{cls_identifier}-#{id}"]
getWmdInput: ($content, $local, cls_identifier) ->
id = $content.attr("_id")
$local("#wmd-input-#{cls_identifier}-#{id}")
getWmdContent: ($content, $local, cls_identifier) ->
Discussion.getWmdInput($content, $local, cls_identifier).val()
setWmdContent: ($content, $local, cls_identifier, text) ->
Discussion.getWmdInput($content, $local, cls_identifier).val(text)
Discussion.getWmdEditor($content, $local, cls_identifier).refreshPreview()
getContentInfo: (id, attr) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
(window.$$annotated_content_info[id] || {})[attr]
setContentInfo: (id, attr, value) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info[id] ||= {}
window.$$annotated_content_info[id][attr] = value
extendContentInfo: (id, newInfo) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info[id] = newInfo
bulkExtendContentInfo: (newInfos) ->
if not window.$$annotated_content_info?
window.$$annotated_content_info = {}
window.$$annotated_content_info = $.extend window.$$annotated_content_info, newInfos
subscriptionLink: (type, id) ->
followLink = ->
Discussion.generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow)
unfollowLink = ->
Discussion.generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow)
handleFollow = (elem) ->
Discussion.safeAjax
$elem: $(elem)
url: Discussion.urlFor("follow_#{type}", id)
type: "POST"
success: (response, textStatus) ->
if textStatus == "success"
$(elem).replaceWith unfollowLink()
dataType: 'json'
handleUnfollow = (elem) ->
Discussion.safeAjax
$elem: $(elem)
url: Discussion.urlFor("unfollow_#{type}", id)
type: "POST"
success: (response, textStatus) ->
if textStatus == "success"
$(elem).replaceWith followLink()
dataType: 'json'
if Discussion.isSubscribed(id, type)
unfollowLink()
else
followLink()
processEachMathAndCode: (text, processor) ->
codeArchive = []
RE_DISPLAYMATH = /^([^\$]*?)\$\$([^\$]*?)\$\$(.*)$/m
RE_INLINEMATH = /^([^\$]*?)\$([^\$]+?)\$(.*)$/m
ESCAPED_DOLLAR = '@@ESCAPED_D@@'
ESCAPED_BACKSLASH = '@@ESCAPED_B@@'
processedText = ""
$div = $("<div>").html(text)
$div.find("code").each (index, code) ->
codeArchive.push $(code).html()
$(code).html(codeArchive.length - 1)
text = $div.html()
text = text.replace /\\\$/g, ESCAPED_DOLLAR
while true
if RE_INLINEMATH.test(text)
text = text.replace RE_INLINEMATH, ($0, $1, $2, $3) ->
processedText += $1 + processor("$" + $2 + "$", 'inline')
$3
else if RE_DISPLAYMATH.test(text)
text = text.replace RE_DISPLAYMATH, ($0, $1, $2, $3) ->
processedText += $1 + processor("$$" + $2 + "$$", 'display')
$3
else
processedText += text
break
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, ($0, $1, $2) ->
processor("\\begin{#{$1}}" + $2 + "\\end{#{$1}}")
text = text.replace(new RegExp(ESCAPED_BACKSLASH, 'g'), '\\\\\\\\')
$div = $("<div>").html(text)
cnt = 0
$div.find("code").each (index, code) ->
$(code).html(processor(codeArchive[cnt], 'code'))
cnt += 1
text = $div.html()
text
...@@ -764,6 +764,10 @@ section.wiki { ...@@ -764,6 +764,10 @@ section.wiki {
overflow: hidden; overflow: hidden;
} }
a.list-children {
margin-left: 3px;
}
tr:nth-child(even) { tr:nth-child(even) {
background: #F6F6F6; background: #F6F6F6;
} }
......
...@@ -29,7 +29,7 @@ $(document).ready(function(){ ...@@ -29,7 +29,7 @@ $(document).ready(function(){
${get_course_info_section(course, 'updates')} ${get_course_info_section(course, 'updates')}
</section> </section>
<section aria-label="Handout Navigation" class="handouts"> <section aria-label="Handout Navigation" class="handouts">
<h1>Course Handouts</h1> <h1>${course.info_sidebar_name}</h1>
${get_course_info_section(course, 'handouts')} ${get_course_info_section(course, 'handouts')}
</section> </section>
% else: % else:
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<%block name="js_extra"> <%block name="js_extra">
<%include file="_js_body_dependencies.html" /> <%include file="_js_body_dependencies.html" />
<%static:js group='discussion'/>
</%block> </%block>
<%include file="../courseware/course_navigation.html" args="active_page='discussion'" /> <%include file="../courseware/course_navigation.html" args="active_page='discussion'" />
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<a href="https://www.edx.org" class="logo"></a> <a href="https://www.edx.org" class="logo"></a>
<a href="${reverse('courses')}">Find Courses</a> <a href="${reverse('courses')}">Find Courses</a>
<a href="${reverse('about_edx')}">About</a> <a href="${reverse('about_edx')}">About</a>
<a href="http://edxonline.tumblr.com/">Blog</a> <a href="http://blog.edx.org/">Blog</a>
<a href="${reverse('jobs')}">Jobs</a> <a href="${reverse('jobs')}">Jobs</a>
<a href="${reverse('contact')}">Contact</a> <a href="${reverse('contact')}">Contact</a>
</section> </section>
......
## mako ## mako
<%! from django.core.urlresolvers import reverse %> <%!
from django.core.urlresolvers import reverse
from wiki.core.permissions import can_change_permissions
%>
<li class="${"active" if selected_tab == "view" else ""}"> <li class="${"active" if selected_tab == "view" else ""}">
<a href="${reverse('wiki:get', kwargs={'article_id' : article.id, 'path' : urlpath.path})}"> <a href="${reverse('wiki:get', kwargs={'article_id' : article.id, 'path' : urlpath.path})}">
...@@ -37,9 +40,10 @@ ...@@ -37,9 +40,10 @@
<%doc> <%doc>
The settings link has been disabled because the notifications app hasn't been integrated yet and those are the only useful settings. This should be enabled for all non-anonymous users once the notifications app has been integrated and styled.
</%doc>
%if not user.is_anonymous(): %if can_change_permissions(article,user):
<li class="${"active" if selected_tab == "settings" else ""}"> <li class="${"active" if selected_tab == "settings" else ""}">
<a href="${reverse('wiki:settings', kwargs={'article_id' : article.id, 'path' : urlpath.path})}"> <a href="${reverse('wiki:settings', kwargs={'article_id' : article.id, 'path' : urlpath.path})}">
<span class="icon-settings icon"></span> <span class="icon-settings icon"></span>
...@@ -47,6 +51,6 @@ The settings link has been disabled because the notifications app hasn't been in ...@@ -47,6 +51,6 @@ The settings link has been disabled because the notifications app hasn't been in
</a> </a>
</li> </li>
%endif %endif
</%doc>
-e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles -e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles
-e git://github.com/MITx/django-pipeline.git#egg=django-pipeline -e git://github.com/MITx/django-pipeline.git#egg=django-pipeline
-e git://github.com/benjaoming/django-wiki.git@3576a2d#egg=django-wiki -e git://github.com/benjaoming/django-wiki.git@533c7fc#egg=django-wiki
-e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev -e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e common/lib/capa -e common/lib/capa
-e common/lib/xmodule -e common/lib/xmodule
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