Commit f6a3842f by Brittany Cheng

merging inline html

parents 45351880 1ad838ac
...@@ -19,6 +19,7 @@ class DiscussionModule(XModule): ...@@ -19,6 +19,7 @@ class DiscussionModule(XModule):
'discussion_id': self.discussion_id, 'discussion_id': self.discussion_id,
'search_bar': '', 'search_bar': '',
'user_info': comment_client.get_user_info(self.user_id, raw=True), 'user_info': comment_client.get_user_info(self.user_id, raw=True),
'tags': comment_client.get_threads_tags(raw=True),
'course_id': self.course_id, 'course_id': self.course_id,
} }
return self.system.render_template('discussion/inline.html', context) return self.system.render_template('discussion/inline.html', context)
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
// The text that appears on the upper part of the dialog box when // The text that appears on the upper part of the dialog box when
// entering links. // entering links.
var linkDialogText = "<p><b>Insert Hyperlink</b></p><p>http://example.com/ \"optional title\"</p>"; var linkDialogText = "<p><b>Insert Hyperlink</b></p><p>http://example.com/ \"optional title\"</p>";
var imageDialogText = "<p><b>Insert Image</b></p><p>http://example.com/images/diagram.jpg \"optional title\"<br><br>Need <a href='http://www.google.com/search?q=free+image+hosting' target='_blank'>free image hosting?</a></p>"; var imageDialogText = "<p><b>Insert Image (upload file or type url)</b></p><p>http://example.com/images/diagram.jpg \"optional title\"<br><br></p>";
// The default text that appears in the dialog input box when entering // The default text that appears in the dialog input box when entering
// links. // links.
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
// - getConverter() returns the markdown converter object that was passed to the constructor // - getConverter() returns the markdown converter object that was passed to the constructor
// - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op. // - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op.
// - refreshPreview() forces the preview to be updated. This method is only available after run() was called. // - refreshPreview() forces the preview to be updated. This method is only available after run() was called.
Markdown.Editor = function (markdownConverter, idPostfix, help) { Markdown.Editor = function (markdownConverter, idPostfix, help, imageUploadHandler) {
idPostfix = idPostfix || ""; idPostfix = idPostfix || "";
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
} }
} }
uiManager = new UIManager(idPostfix, panels, undoManager, previewManager, commandManager, help); uiManager = new UIManager(idPostfix, panels, undoManager, previewManager, commandManager, help, imageUploadHandler);
uiManager.setUndoRedoButtonStates(); uiManager.setUndoRedoButtonStates();
var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); }; var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); };
...@@ -1010,7 +1010,7 @@ ...@@ -1010,7 +1010,7 @@
// callback: The function which is executed when the prompt is dismissed, either via OK or Cancel. // callback: The function which is executed when the prompt is dismissed, either via OK or Cancel.
// It receives a single argument; either the entered text (if OK was chosen) or null (if Cancel // It receives a single argument; either the entered text (if OK was chosen) or null (if Cancel
// was chosen). // was chosen).
ui.prompt = function (text, defaultInputText, callback) { ui.prompt = function (text, defaultInputText, callback, imageUploadHandler) {
// These variables need to be declared at this level since they are used // These variables need to be declared at this level since they are used
// in multiple functions. // in multiple functions.
...@@ -1044,9 +1044,11 @@ ...@@ -1044,9 +1044,11 @@
else { else {
// Fixes common pasting errors. // Fixes common pasting errors.
text = text.replace(/^http:\/\/(https?|ftp):\/\//, '$1://'); text = text.replace(/^http:\/\/(https?|ftp):\/\//, '$1://');
if (!/^(?:https?|ftp):\/\//.test(text)) // doesn't change url if started with '/' (local)
if (!/^(?:https?|ftp):\/\//.test(text) && text.charAt(0) != '/') {
text = 'http://' + text; text = 'http://' + text;
} }
}
dialog.parentNode.removeChild(dialog); dialog.parentNode.removeChild(dialog);
...@@ -1095,6 +1097,21 @@ ...@@ -1095,6 +1097,21 @@
style.marginLeft = style.marginRight = "auto"; style.marginLeft = style.marginRight = "auto";
form.appendChild(input); form.appendChild(input);
// The choose file button if prompt type is 'image'
if (imageUploadHandler) {
var chooseFile = doc.createElement("input");
chooseFile.type = "file";
chooseFile.name = "file-upload";
chooseFile.id = "file-upload";
chooseFile.onchange = function() {
imageUploadHandler(this, input);
};
form.appendChild(doc.createElement("br"));
form.appendChild(chooseFile);
}
// The ok button // The ok button
var okButton = doc.createElement("input"); var okButton = doc.createElement("input");
okButton.type = "button"; okButton.type = "button";
...@@ -1160,7 +1177,7 @@ ...@@ -1160,7 +1177,7 @@
}, 0); }, 0);
}; };
function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions) { function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions, imageUploadHandler) {
var inputBox = panels.input, var inputBox = panels.input,
buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements. buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements.
...@@ -1419,7 +1436,7 @@ ...@@ -1419,7 +1436,7 @@
buttons.quote = makeButton("wmd-quote-button", "Blockquote <blockquote> Ctrl+Q", "-60px", bindCommand("doBlockquote")); buttons.quote = makeButton("wmd-quote-button", "Blockquote <blockquote> Ctrl+Q", "-60px", bindCommand("doBlockquote"));
buttons.code = makeButton("wmd-code-button", "Code Sample <pre><code> Ctrl+K", "-80px", bindCommand("doCode")); buttons.code = makeButton("wmd-code-button", "Code Sample <pre><code> Ctrl+K", "-80px", bindCommand("doCode"));
buttons.image = makeButton("wmd-image-button", "Image <img> Ctrl+G", "-100px", bindCommand(function (chunk, postProcessing) { buttons.image = makeButton("wmd-image-button", "Image <img> Ctrl+G", "-100px", bindCommand(function (chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, true); return this.doLinkOrImage(chunk, postProcessing, true, imageUploadHandler);
})); }));
makeSpacer(2); makeSpacer(2);
buttons.olist = makeButton("wmd-olist-button", "Numbered List <ol> Ctrl+O", "-120px", bindCommand(function (chunk, postProcessing) { buttons.olist = makeButton("wmd-olist-button", "Numbered List <ol> Ctrl+O", "-120px", bindCommand(function (chunk, postProcessing) {
...@@ -1649,7 +1666,7 @@ ...@@ -1649,7 +1666,7 @@
}); });
} }
commandProto.doLinkOrImage = function (chunk, postProcessing, isImage) { commandProto.doLinkOrImage = function (chunk, postProcessing, isImage, imageUploadHandler) {
chunk.trimWhitespace(); chunk.trimWhitespace();
chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/); chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/);
...@@ -1724,7 +1741,7 @@ ...@@ -1724,7 +1741,7 @@
if (isImage) { if (isImage) {
if (!this.hooks.insertImageDialog(linkEnteredCallback)) if (!this.hooks.insertImageDialog(linkEnteredCallback))
ui.prompt(imageDialogText, imageDefaultText, linkEnteredCallback); ui.prompt(imageDialogText, imageDefaultText, linkEnteredCallback, imageUploadHandler);
} }
else { else {
ui.prompt(linkDialogText, linkDefaultText, linkEnteredCallback); ui.prompt(linkDialogText, linkDefaultText, linkEnteredCallback);
......
jQuery.extend({
handleError: function( s, xhr, status, e ) {
// If a local callback was specified, fire it
if ( s.error ) {
s.error.call( s.context || s, xhr, status, e );
}
// Fire the global callback
if ( s.global ) {
(s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
}
},
createUploadIframe: function(id, uri){
//create frame
var frameId = 'jUploadFrame' + id;
if(window.ActiveXObject) {
var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
if(typeof uri== 'boolean'){
io.src = 'javascript:false';
}
else if(typeof uri== 'string'){
io.src = uri;
}
}
else {
var io = document.createElement('iframe');
io.id = frameId;
io.name = frameId;
}
io.style.position = 'absolute';
io.style.top = '-1000px';
io.style.left = '-1000px';
document.body.appendChild(io);
return io;
},
createUploadForm: function(id, fileElementId)
{
//create form
var formId = 'jUploadForm' + id;
var fileId = 'jUploadFile' + id;
var form = $('<form action="" method="POST" name="' + formId + '" id="' + formId
+ '" enctype="multipart/form-data"></form>');
var oldElement = $('#' + fileElementId);
var newElement = $(oldElement).clone();
$(oldElement).attr('id', fileId);
$(oldElement).before(newElement);
$(oldElement).appendTo(form);
//set attributes
$(form).css('position', 'absolute');
$(form).css('top', '-1200px');
$(form).css('left', '-1200px');
$(form).appendTo('body');
return form;
},
ajaxFileUpload: function(s) {
// TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
s = jQuery.extend({}, jQuery.ajaxSettings, s);
var id = new Date().getTime()
var form = jQuery.createUploadForm(id, s.fileElementId);
var io = jQuery.createUploadIframe(id, s.secureuri);
var frameId = 'jUploadFrame' + id;
var formId = 'jUploadForm' + id;
// Watch for a new set of requests
if ( s.global && ! jQuery.active++ )
{
jQuery.event.trigger( "ajaxStart" );
}
var requestDone = false;
// Create the request object
var xml = {}
if ( s.global )
jQuery.event.trigger("ajaxSend", [xml, s]);
// Wait for a response to come back
var uploadCallback = function(isTimeout)
{
var io = document.getElementById(frameId);
try {
if(io.contentWindow){
xml.responseText = io.contentWindow.document.body ?
io.contentWindow.document.body.innerText : null;
xml.responseXML = io.contentWindow.document.XMLDocument ?
io.contentWindow.document.XMLDocument : io.contentWindow.document;
}
else if(io.contentDocument)
{
xml.responseText = io.contentDocument.document.body ?
io.contentDocument.document.body.textContent || document.body.innerText : null;
xml.responseXML = io.contentDocument.document.XMLDocument ?
io.contentDocument.document.XMLDocument : io.contentDocument.document;
}
}
catch(e)
{
jQuery.handleError(s, xml, null, e);
}
if ( xml || isTimeout == "timeout")
{
requestDone = true;
var status;
try {
status = isTimeout != "timeout" ? "success" : "error";
// Make sure that the request was successful or notmodified
if ( status != "error" )
{
// process the data (runs the xml through httpData regardless of callback)
var data = jQuery.uploadHttpData( xml, s.dataType );
// If a local callback was specified, fire it and pass it the data
if ( s.success )
s.success( data, status );
// Fire the global callback
if( s.global )
jQuery.event.trigger( "ajaxSuccess", [xml, s] );
} else
jQuery.handleError(s, xml, status);
} catch(e)
{
status = "error";
jQuery.handleError(s, xml, status, e);
}
// The request was completed
if( s.global )
jQuery.event.trigger( "ajaxComplete", [xml, s] );
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
// Process result
if ( s.complete )
s.complete(xml, status);
jQuery(io).unbind();
setTimeout(function()
{ try
{
$(io).remove();
$(form).remove();
} catch(e) {
jQuery.handleError(s, xml, null, e);
}
}, 100)
xml = null;
}
}
// Timeout checker
if ( s.timeout > 0 ) {
setTimeout(function(){
// Check to see if the request is still happening
if( !requestDone ) uploadCallback( "timeout" );
}, s.timeout);
}
try
{
// var io = $('#' + frameId);
var form = $('#' + formId);
$(form).attr('action', s.url);
$(form).attr('method', 'POST');
$(form).attr('target', frameId);
if(form.encoding)
{
form.encoding = 'multipart/form-data';
}
else
{
form.enctype = 'multipart/form-data';
}
$(form).submit();
} catch(e)
{
jQuery.handleError(s, xml, null, e);
}
if(window.attachEvent){
document.getElementById(frameId).attachEvent('onload', uploadCallback);
}
else{
document.getElementById(frameId).addEventListener('load', uploadCallback, false);
}
return {abort: function () {}};
},
uploadHttpData: function( r, type ) {
var data = !type;
data = type == "xml" || data ? r.responseXML : r.responseText;
// If the type is "script", eval it in global context
if ( type == "script" )
jQuery.globalEval( data );
// Get the JavaScript object, if JSON is used.
if ( type == "json" )
eval( "data = " + data );
// evaluate scripts within html
if ( type == "html" )
jQuery("<div>").html(data).evalScripts();
//alert($('param', data).each(function(){alert($(this).attr('value'));}));
return data;
}
})
...@@ -3,6 +3,7 @@ import django_comment_client.base.views ...@@ -3,6 +3,7 @@ import django_comment_client.base.views
urlpatterns = patterns('django_comment_client.base.views', urlpatterns = patterns('django_comment_client.base.views',
url(r'upload$', 'upload', name='upload'),
url(r'threads/(?P<thread_id>[\w\-]+)/update$', 'update_thread', name='update_thread'), url(r'threads/(?P<thread_id>[\w\-]+)/update$', 'update_thread', name='update_thread'),
url(r'threads/(?P<thread_id>[\w\-]+)/reply$', 'create_comment', name='create_comment'), url(r'threads/(?P<thread_id>[\w\-]+)/reply$', 'create_comment', name='create_comment'),
url(r'threads/(?P<thread_id>[\w\-]+)/delete', 'delete_thread', name='delete_thread'), url(r'threads/(?P<thread_id>[\w\-]+)/delete', 'delete_thread', name='delete_thread'),
......
import time
import random
import os
import os.path
import logging
import urlparse
import comment_client
from django.core import exceptions
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST, require_GET from django.views.decorators.http import require_POST, require_GET
from django.http import HttpResponse from django.http import HttpResponse
from django.utils import simplejson from django.utils import simplejson
from django.views.decorators import csrf
import comment_client from django.core.files.storage import get_storage_class
from django.utils.translation import ugettext as _
from django.conf import settings
class JsonResponse(HttpResponse): class JsonResponse(HttpResponse):
def __init__(self, data=None): def __init__(self, data=None):
...@@ -183,3 +195,82 @@ def search(request, course_id): ...@@ -183,3 +195,82 @@ def search(request, course_id):
commentable_id = request.GET.get('commentable_id', None) commentable_id = request.GET.get('commentable_id', None)
response = comment_client.search(text, commentable_id) response = comment_client.search(text, commentable_id)
return JsonResponse(response) return JsonResponse(response)
@csrf.csrf_exempt
@login_required
@require_POST
def upload(request, course_id):#ajax upload file to a question or answer
"""view that handles file upload via Ajax
"""
# check upload permission
result = ''
error = ''
new_file_name = ''
try:
# TODO authorization
#may raise exceptions.PermissionDenied
#if request.user.is_anonymous():
# msg = _('Sorry, anonymous users cannot upload files')
# raise exceptions.PermissionDenied(msg)
#request.user.assert_can_upload_file()
# check file type
f = request.FILES['file-upload']
file_extension = os.path.splitext(f.name)[1].lower()
if not file_extension in settings.DISCUSSION_ALLOWED_UPLOAD_FILE_TYPES:
file_types = "', '".join(settings.DISCUSSION_ALLOWED_UPLOAD_FILE_TYPES)
msg = _("allowed file types are '%(file_types)s'") % \
{'file_types': file_types}
raise exceptions.PermissionDenied(msg)
# generate new file name
new_file_name = str(
time.time()
).replace(
'.',
str(random.randint(0,100000))
) + file_extension
file_storage = get_storage_class()()
# use default storage to store file
file_storage.save(new_file_name, f)
# check file size
# byte
size = file_storage.size(new_file_name)
if size > settings.ASKBOT_MAX_UPLOAD_FILE_SIZE:
file_storage.delete(new_file_name)
msg = _("maximum upload file size is %(file_size)sK") % \
{'file_size': settings.ASKBOT_MAX_UPLOAD_FILE_SIZE}
raise exceptions.PermissionDenied(msg)
except exceptions.PermissionDenied, e:
error = unicode(e)
except Exception, e:
logging.critical(unicode(e))
error = _('Error uploading file. Please contact the site administrator. Thank you.')
if error == '':
result = 'Good'
file_url = file_storage.url(new_file_name)
parsed_url = urlparse.urlparse(file_url)
file_url = urlparse.urlunparse(
urlparse.ParseResult(
parsed_url.scheme,
parsed_url.netloc,
parsed_url.path,
'', '', ''
)
)
else:
result = ''
file_url = ''
return JsonResponse({
'result': {
'msg': result,
'error': error,
'file_url': file_url,
}
})
...@@ -32,6 +32,7 @@ def render_discussion(request, course_id, threads, discussion_id=None, search_te ...@@ -32,6 +32,7 @@ def render_discussion(request, course_id, threads, discussion_id=None, search_te
'discussion_id': discussion_id, 'discussion_id': discussion_id,
'search_bar': render_search_bar(request, course_id, discussion_id, text=search_text), 'search_bar': render_search_bar(request, course_id, discussion_id, text=search_text),
'user_info': comment_client.get_user_info(request.user.id, raw=True), 'user_info': comment_client.get_user_info(request.user.id, raw=True),
'tags': comment_client.get_threads_tags(raw=True),
'course_id': course_id, 'course_id': course_id,
} }
return render_to_string('discussion/inline.html', context) return render_to_string('discussion/inline.html', context)
...@@ -78,6 +79,7 @@ def render_single_thread(request, course_id, thread_id): ...@@ -78,6 +79,7 @@ def render_single_thread(request, course_id, thread_id):
context = { context = {
'thread': comment_client.get_thread(thread_id, recursive=True), 'thread': comment_client.get_thread(thread_id, recursive=True),
'user_info': comment_client.get_user_info(request.user.id, raw=True), 'user_info': comment_client.get_user_info(request.user.id, raw=True),
'tags': comment_client.get_threads_tags(raw=True),
'course_id': course_id, 'course_id': course_id,
} }
return render_to_string('discussion/single_thread.html', context) return render_to_string('discussion/single_thread.html', context)
...@@ -91,7 +93,6 @@ def single_thread(request, course_id, thread_id): ...@@ -91,7 +93,6 @@ def single_thread(request, course_id, thread_id):
'init': '', 'init': '',
'content': render_single_thread(request, course_id, thread_id), 'content': render_single_thread(request, course_id, thread_id),
'accordion': '', 'accordion': '',
'user_info': comment_client.get_user_info(request.user.id, raw=True),
'course': course, 'course': course,
} }
......
...@@ -30,6 +30,7 @@ import djcelery ...@@ -30,6 +30,7 @@ import djcelery
from path import path from path import path
from .askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from from .askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from
from .discussionsettings import *
################################### FEATURES ################################### ################################### FEATURES ###################################
COURSEWARE_ENABLED = True COURSEWARE_ENABLED = True
......
DISCUSSION_ALLOWED_UPLOAD_FILE_TYPES = ('.jpg', '.jpeg', '.gif', '.bmp', '.png', '.tiff')
...@@ -11,6 +11,9 @@ def delete_threads(commentable_id, *args, **kwargs): ...@@ -11,6 +11,9 @@ def delete_threads(commentable_id, *args, **kwargs):
def get_threads(commentable_id, recursive=False, *args, **kwargs): def get_threads(commentable_id, recursive=False, *args, **kwargs):
return _perform_request('get', _url_for_threads(commentable_id), {'recursive': recursive}, *args, **kwargs) return _perform_request('get', _url_for_threads(commentable_id), {'recursive': recursive}, *args, **kwargs)
def get_threads_tags(*args, **kwargs):
return _perform_request('get', _url_for_threads_tags(), {}, *args, **kwargs)
def create_thread(commentable_id, attributes, *args, **kwargs): def create_thread(commentable_id, attributes, *args, **kwargs):
return _perform_request('post', _url_for_threads(commentable_id), attributes, *args, **kwargs) return _perform_request('post', _url_for_threads(commentable_id), attributes, *args, **kwargs)
...@@ -126,3 +129,6 @@ def _url_for_user(user_id): ...@@ -126,3 +129,6 @@ def _url_for_user(user_id):
def _url_for_search(): def _url_for_search():
return "{prefix}/search".format(prefix=PREFIX) return "{prefix}/search".format(prefix=PREFIX)
def _url_for_threads_tags():
return "{prefix}/threads/tags".format(prefix=PREFIX)
...@@ -102,6 +102,7 @@ $ -> ...@@ -102,6 +102,7 @@ $ ->
text text
if Markdown? if Markdown?
Markdown.getMathCompatibleConverter = -> Markdown.getMathCompatibleConverter = ->
converter = Markdown.getSanitizingConverter() converter = Markdown.getSanitizingConverter()
processor = new MathJaxProcessor() processor = new MathJaxProcessor()
...@@ -109,11 +110,13 @@ $ -> ...@@ -109,11 +110,13 @@ $ ->
converter.hooks.chain "postConversion", processor.replaceMath converter.hooks.chain "postConversion", processor.replaceMath
converter converter
Markdown.makeWmdEditor = (elem, appended_id) -> Markdown.makeWmdEditor = (elem, appended_id, imageUploadUrl) ->
$elem = $(elem) $elem = $(elem)
if not $elem.length if not $elem.length
console.log "warning: elem for makeWmdEditor doesn't exist" console.log "warning: elem for makeWmdEditor doesn't exist"
return return
if not $elem.find(".wmd-panel").length if not $elem.find(".wmd-panel").length
_append = appended_id || "" _append = appended_id || ""
$wmdPanel = $("<div>").addClass("wmd-panel") $wmdPanel = $("<div>").addClass("wmd-panel")
...@@ -121,8 +124,42 @@ $ -> ...@@ -121,8 +124,42 @@ $ ->
.append($("<textarea>").addClass("wmd-input").attr("id", "wmd-input#{_append}")) .append($("<textarea>").addClass("wmd-input").attr("id", "wmd-input#{_append}"))
.append($("<div>").attr("id", "wmd-preview#{_append}").addClass("wmd-panel wmd-preview")) .append($("<div>").attr("id", "wmd-preview#{_append}").addClass("wmd-panel wmd-preview"))
$elem.append($wmdPanel) $elem.append($wmdPanel)
converter = Markdown.getMathCompatibleConverter() converter = Markdown.getMathCompatibleConverter()
editor = new Markdown.Editor(converter, appended_id)
ajaxFileUpload = (imageUploadUrl, input, startUploadHandler) ->
$("#loading").ajaxStart(-> $(this).show()).ajaxComplete(-> $(this).hide())
$("#upload").ajaxStart(-> $(this).hide()).ajaxComplete(-> $(this).show())
$.ajaxFileUpload
url: imageUploadUrl
secureuri: false
fileElementId: 'file-upload'
dataType: 'json'
success: (data, status) ->
fileURL = data['result']['file_url']
error = data['result']['error']
if error != ''
alert error
if startUploadHandler
$('#file-upload').unbind('change').change(startUploadHandler)
console.log error
else
$(input).attr('value', fileURL)
error: (data, status, e) ->
alert(e)
if startUploadHandler
$('#file-upload').unbind('change').change(startUploadHandler)
imageUploadHandler = (elem, input) ->
console.log "here"
ajaxFileUpload(imageUploadUrl, input, imageUploadHandler)
editor = new Markdown.Editor(
converter,
appended_id, # idPostfix
null, # help handler
imageUploadHandler
)
delayRenderer = new MathJaxDelayRenderer() delayRenderer = new MathJaxDelayRenderer()
editor.hooks.chain "onPreviewPush", (text, previewSet) -> editor.hooks.chain "onPreviewPush", (text, previewSet) ->
delayRenderer.render delayRenderer.render
......
...@@ -43,6 +43,7 @@ Discussion = ...@@ -43,6 +43,7 @@ Discussion =
delete_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/delete" delete_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/delete"
upvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/upvote" upvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/upvote"
downvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/downvote" downvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/downvote"
upload : "/courses/#{$$course_id}/discussion/upload"
search : "/courses/#{$$course_id}/discussion/forum/search" search : "/courses/#{$$course_id}/discussion/forum/search"
}[name] }[name]
...@@ -89,7 +90,7 @@ Discussion = ...@@ -89,7 +90,7 @@ Discussion =
newPostBody = $(discussion).find(".new-post-body") newPostBody = $(discussion).find(".new-post-body")
if newPostBody.length if newPostBody.length
Markdown.makeWmdEditor newPostBody, "-new-post-body-#{$(discussion).attr('_id')}" Markdown.makeWmdEditor newPostBody, "-new-post-body-#{$(discussion).attr('_id')}", Discussion.urlFor('upload')
initializeWatchThreads = (index, thread) -> initializeWatchThreads = (index, thread) ->
$thread = $(thread) $thread = $(thread)
...@@ -175,7 +176,7 @@ Discussion = ...@@ -175,7 +176,7 @@ Discussion =
$discussionContent.append(editView) $discussionContent.append(editView)
Markdown.makeWmdEditor $local(".comment-edit"), "-comment-edit-#{id}" Markdown.makeWmdEditor $local(".comment-edit"), "-comment-edit-#{id}", Discussion.urlFor('upload')
cancelReply = generateDiscussionLink("discussion-cancel-reply", "Cancel", handleCancelReply) cancelReply = generateDiscussionLink("discussion-cancel-reply", "Cancel", handleCancelReply)
submitReply = generateDiscussionLink("discussion-submit-reply", "Submit", handleSubmitReply) submitReply = generateDiscussionLink("discussion-submit-reply", "Submit", handleSubmitReply)
$local(".discussion-link").hide() $local(".discussion-link").hide()
...@@ -230,10 +231,17 @@ Discussion = ...@@ -230,10 +231,17 @@ Discussion =
$local(".discussion-vote-down").click -> $local(".discussion-vote-down").click ->
handleVote(this, "down") handleVote(this, "down")
initializeContent: (content) ->
$content = $(content)
$local = generateLocal($content.children(".discussion-content"))
raw_text = $local(".content-body").html()
converter = Markdown.getMathCompatibleConverter()
$local(".content-body").html(converter.makeHtml(raw_text))
bindDiscussionEvents: (discussion) -> bindDiscussionEvents: (discussion) ->
$discussion = $(discussion) $discussion = $(discussion)
$discussionNonContent = $discussion.children(".discussion-non-content") $discussionNonContent = $discussion.children(".discussion-non-content")
$local = (selector) -> $discussionNonContent.find(selector) $local = generateLocal($discussionNonContent)#(selector) -> $discussionNonContent.find(selector)
id = $discussion.attr("_id") id = $discussion.attr("_id")
...@@ -266,7 +274,9 @@ Discussion = ...@@ -266,7 +274,9 @@ Discussion =
$local(".new-post-form").submit() $local(".new-post-form").submit()
$discussion.find(".thread").each (index, thread) -> $discussion.find(".thread").each (index, thread) ->
Discussion.initializeContent(thread)
Discussion.bindContentEvents(thread) Discussion.bindContentEvents(thread)
$discussion.find(".comment").each (index, comment) -> $discussion.find(".comment").each (index, comment) ->
Discussion.initializeContent(comment)
Discussion.bindContentEvents(comment) Discussion.bindContentEvents(comment)
...@@ -28,7 +28,28 @@ ...@@ -28,7 +28,28 @@
<%static:js group='courseware'/> <%static:js group='courseware'/>
<%include file="mathjax_include.html" /> <script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [
["$","$"],
],
displayMath: [
["$$","$$"],
]
}
});
</script>
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
MathJax extension libraries -->
<script type="text/javascript" src="/static/js/vendor/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-MML-AM_HTMLorMML-full"></script>
<script type="text/javascript" src="${static.url('js/vendor/split.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/jquery.ajaxfileupload.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Converter.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Sanitizer.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Editor.js')}"></script>
<!-- TODO: http://docs.jquery.com/Plugins/Validation --> <!-- TODO: http://docs.jquery.com/Plugins/Validation -->
<script type="text/javascript"> <script type="text/javascript">
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
## It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of MathJax extension libraries ## It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of MathJax extension libraries
<script type="text/javascript" src="/static/js/vendor/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-MML-AM_HTMLorMML-full"></script> <script type="text/javascript" src="/static/js/vendor/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-MML-AM_HTMLorMML-full"></script>
<script type="text/javascript" src="${static.url('js/vendor/split.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/split.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/jquery.ajaxfileupload.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Converter.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/Markdown.Converter.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Sanitizer.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/Markdown.Sanitizer.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/Markdown.Editor.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/Markdown.Editor.js')}"></script>
......
...@@ -7,11 +7,10 @@ ...@@ -7,11 +7,10 @@
</div> </div>
${search_bar} ${search_bar}
<form class="new-post-form" _id="${discussion_id}"> <form class="new-post-form" _id="${discussion_id}">
<input class="searchInput" type="text" autocomplete="off" placeholder="Comment Text"/> <input type="text" class="new-post-title" placeholder="Title"/>
<div class="new-post-body"> <div class="new-post-body"></div>
</div> <input type="text" class="new-pot-tags" placeholder="tag1, tag2"/>
<a class="discussion-new-post" href="javascript:void(0)">New Post</a> <a class="discussion-new-post" href="javascript:void(0)">New Post</a>
</form> </form>
</div> </div>
% for thread in threads: % for thread in threads:
...@@ -19,7 +18,13 @@ ...@@ -19,7 +18,13 @@
% endfor % endfor
</section> </section>
<%!
def escape_quotes(text):
return text.replace('\"', '\\\"').replace("\'", "\\\'")
%>
<script type="text/javascript"> <script type="text/javascript">
var $$user_info = JSON.parse('${user_info}'); var $$user_info = JSON.parse('${user_info | escape_quotes}');
var $$course_id = "${course_id}"; var $$course_id = "${course_id}";
var $$tags = JSON.parse("${tags | escape_quotes}");
</script> </script>
...@@ -5,7 +5,13 @@ ...@@ -5,7 +5,13 @@
${renderer.render_thread(course_id, thread, edit_thread=True, show_comments=True)} ${renderer.render_thread(course_id, thread, edit_thread=True, show_comments=True)}
</section> </section>
<%!
def escape_quotes(text):
return text.replace('\"', '\\\"').replace("\'", "\\\'")
%>
<script type="text/javascript"> <script type="text/javascript">
var $$user_info = JSON.parse('${user_info}'); var $$user_info = JSON.parse('${user_info | escape_quotes}');
var $$course_id = "${course_id}"; var $$course_id = "${course_id}";
var $$tags = JSON.parse("${tags | escape_quotes}");
</script> </script>
...@@ -15,9 +15,9 @@ ...@@ -15,9 +15,9 @@
<div class="discussion-upper-wrapper clearfix"> <div class="discussion-upper-wrapper clearfix">
${render_vote(thread)} ${render_vote(thread)}
<div class="discussion-right-wrapper clearfix"> <div class="discussion-right-wrapper clearfix">
<a class="thread-title" name="${thread['id']}" href="${url_for_thread}">${thread['title']}</a> <a class="thread-title" name="${thread['id']}" href="${url_for_thread}">${thread['title'] | h}</a>
<div class="discussion-content-view"> <div class="discussion-content-view">
<div class="thread-body">${thread['body']}</div> <div class="content-body thread-body">${thread['body'] | h}</div>
<div class="info"> <div class="info">
${render_info(thread)} ${render_info(thread)}
% if edit_thread: % if edit_thread:
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
${render_vote(comment)} ${render_vote(comment)}
<div class="discussion-right-wrapper"> <div class="discussion-right-wrapper">
<div class="discussion-content-view"> <div class="discussion-content-view">
<a class="comment-body" name="${comment['id']}">${comment['body']}</a> <a class="content-body comment-body" name="${comment['id']}">${comment['body'] | h}</a>
<div class="info"> <div class="info">
${render_info(comment)} ${render_info(comment)}
${render_reply()} ${render_reply()}
......
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