From a3757016d9e30dc998062b34203cca40fa78573f Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 21 Aug 2013 22:36:02 -0400 Subject: [PATCH] FIX: Quotes with new lines were broken --- .../discourse/dialects/bbcode_dialect.js | 102 ++++++++++++------ .../discourse/views/composer_view.js | 9 +- test/javascripts/components/bbcode_test.js | 8 +- test/javascripts/components/markdown_test.js | 14 ++- 4 files changed, 86 insertions(+), 47 deletions(-) diff --git a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js index 1c4c38ea6b1..9ac4475af1a 100644 --- a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js +++ b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js @@ -107,46 +107,80 @@ Discourse.Dialect.on("register", function(event) { @return {Array} the JsonML containing the markup or undefined if nothing changed. @namespace Discourse.Dialect **/ - dialect.inline["[quote="] = function bbcodeQuote(text, orig_match) { - var bbcodePattern = new RegExp("\\[quote=?([^\\[\\]]+)?\\]([\\s\\S]*?)\\[\\/quote\\]", "igm"), - m = bbcodePattern.exec(text); + dialect.block['quote'] = function bbcodeQuote(block, next) { + var m = new RegExp("\\[quote=?([^\\[\\]]+)?\\]([\\s\\S]*)", "igm").exec(block); + if (m) { + var paramsString = m[1].replace(/\"/g, ''), + params = {'class': 'quote'}, + paramsSplit = paramsString.split(/\, */), + username = paramsSplit[0], + opts = dialect.options, + startPos = block.indexOf(m[0]), + leading, + quoteContents = [], + result = []; - if (!m) { return; } - var paramsString = m[1].replace(/\"/g, ''), - params = {'class': 'quote'}, - paramsSplit = paramsString.split(/\, */), - username = paramsSplit[0], - opts = dialect.options; + if (startPos > 0) { + leading = block.slice(0, startPos); - paramsSplit.forEach(function(p,i) { - if (i > 0) { - var assignment = p.split(':'); - if (assignment[0] && assignment[1]) { - params['data-' + assignment[0]] = assignment[1].trim(); + var para = ['p']; + this.processInline(leading).forEach(function (l) { + para.push(l); + }); + + result.push(para); + } + + paramsSplit.forEach(function(p,i) { + if (i > 0) { + var assignment = p.split(':'); + if (assignment[0] && assignment[1]) { + params['data-' + assignment[0]] = assignment[1].trim(); + } + } + }); + + var avatarImg; + if (opts.lookupAvatarByPostNumber) { + // client-side, we can retrieve the avatar from the post + var postNumber = parseInt(params['data-post'], 10); + avatarImg = opts.lookupAvatarByPostNumber(postNumber); + } else if (opts.lookupAvatar) { + // server-side, we need to lookup the avatar from the username + avatarImg = opts.lookupAvatar(username); + } + + if (m[2]) { next.unshift(MD.mk_block(m[2])); } + + while (next.length > 0) { + var b = next.shift(), + n = b.match(/([\s\S]*)\[\/quote\]([\s\S]*)/m); + + if (n) { + if (n[2]) { + next.unshift(MD.mk_block(n[2])); + } + quoteContents.push(n[1]); + break; + } else { + quoteContents.push(b); } } - }); - var avatarImg; - if (opts.lookupAvatarByPostNumber) { - // client-side, we can retrieve the avatar from the post - var postNumber = parseInt(params['data-post'], 10); - avatarImg = opts.lookupAvatarByPostNumber(postNumber); - } else if (opts.lookupAvatar) { - // server-side, we need to lookup the avatar from the username - avatarImg = opts.lookupAvatar(username); + var contents = this.processInline(quoteContents.join(" \n \n")); + contents.unshift('blockquote'); + + + result.push(['p', ['aside', params, + ['div', {'class': 'title'}, + ['div', {'class': 'quote-controls'}], + avatarImg ? avatarImg + "\n" : "", + I18n.t('user.said',{username: username}) + ], + contents + ]]); + return result; } - - var quote = ['aside', params, - ['div', {'class': 'title'}, - ['div', {'class': 'quote-controls'}], - avatarImg ? avatarImg + "\n" : "", - I18n.t('user.said',{username: username}) - ], - ['blockquote'].concat(this.processInline(m[2])) - ]; - - return [m[0].length, quote]; }; }); diff --git a/app/assets/javascripts/discourse/views/composer_view.js b/app/assets/javascripts/discourse/views/composer_view.js index 57fada42edb..707c5df7892 100644 --- a/app/assets/javascripts/discourse/views/composer_view.js +++ b/app/assets/javascripts/discourse/views/composer_view.js @@ -192,9 +192,12 @@ Discourse.ComposerView = Discourse.View.extend({ this.editor = editor = Discourse.Markdown.createEditor({ lookupAvatarByPostNumber: function(postNumber) { - var quotedPost = composerView.get('controller.controllers.topic.postStream.posts').findProperty("post_number", postNumber); - if (quotedPost) { - return Discourse.Utilities.tinyAvatar(quotedPost.get("avatar_template")); + var posts = composerView.get('controller.controllers.topic.postStream.posts'); + if (posts) { + var quotedPost = posts.findProperty("post_number", postNumber); + if (quotedPost) { + return Discourse.Utilities.tinyAvatar(quotedPost.get("avatar_template")); + } } } }); diff --git a/test/javascripts/components/bbcode_test.js b/test/javascripts/components/bbcode_test.js index dc04b8e56d2..d198ffd92e2 100644 --- a/test/javascripts/components/bbcode_test.js +++ b/test/javascripts/components/bbcode_test.js @@ -88,15 +88,9 @@ test("quote formatting", function() { format("[quote=\"eviltrout, post:1, topic:1\"]abc[/quote]\nhello", "
\nhello", + "
abc

\n\n

\nhello", "handles new lines properly"); - format("before[quote=\"eviltrout, post:1, topic:1\"]first[/quote]middle[quote=\"eviltrout, post:2, topic:2\"]second[/quote]after", - "before


middle
after", - "can handle more than one quote"); - }); diff --git a/test/javascripts/components/markdown_test.js b/test/javascripts/components/markdown_test.js index ff7fb0d586e..cce57a4412a 100644 --- a/test/javascripts/components/markdown_test.js +++ b/test/javascripts/components/markdown_test.js @@ -85,15 +85,23 @@ test("Links", function() { }); test("Quotes", function() { + + cookedOptions("[quote=\"eviltrout, post: 1\"]\na quote\n\nsecond line\n[/quote]", + { topicId: 2 }, + "

", + "works with multiple lines"); + cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2", { topicId: 2, lookupAvatar: function(name) { return "" + name; } }, - "

1


2

", + "

1

\n\n

\n\n

2

", "handles quotes properly"); cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2", { topicId: 2, lookupAvatar: function(name) { } }, - "

1


2

", + "

1

\n\n

\n\n

2

", "includes no avatar if none is found"); });