From 73946e540235c93a4fa8a7ea8fbe0be18a1014a6 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Tue, 27 May 2014 21:46:31 -0500 Subject: [PATCH] quoting fixes - allow bbcode quotes to be nested - don't allow the '=' to be omitted from quotes - fix some css that made assumptions about nested quotes --- .../discourse/dialects/bbcode_dialect.js | 1 + .../javascripts/discourse/dialects/dialect.js | 50 +++++++++++++------ .../discourse/dialects/quote_dialect.js | 34 +++++++++---- .../stylesheets/desktop/topic-post.scss | 6 ++- test/javascripts/lib/bbcode_test.js | 2 +- 5 files changed, 64 insertions(+), 29 deletions(-) diff --git a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js index d47698d8b68..53016958921 100644 --- a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js +++ b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js @@ -105,6 +105,7 @@ replaceBBCodeParams("size", function(param, contents) { Discourse.Dialect.replaceBlock({ start: /(\[code\])([\s\S]*)/igm, stop: '[/code]', + rawContents: true, emitter: function(blockContents) { return ['p', ['pre'].concat(blockContents.join("\n"))]; diff --git a/app/assets/javascripts/discourse/dialects/dialect.js b/app/assets/javascripts/discourse/dialects/dialect.js index c3071ae35c3..6875b3e54be 100644 --- a/app/assets/javascripts/discourse/dialects/dialect.js +++ b/app/assets/javascripts/discourse/dialects/dialect.js @@ -305,6 +305,7 @@ Discourse.Dialect = { Discourse.Dialect.replaceBlock({ start: /(\[code\])([\s\S]*)/igm, stop: '[/code]', + rawContents: true, emitter: function(blockContents) { return ['p', ['pre'].concat(blockContents)]; @@ -314,9 +315,10 @@ Discourse.Dialect = { @method replaceBlock @param {Object} args Our replacement options - @param {String} [opts.start] The starting regexp we want to find - @param {String} [opts.stop] The ending token we want to find - @param {Function} [opts.emitter] The emitting function to transform the contents of the block into jsonML + @param {RegExp} [args.start] The starting regexp we want to find + @param {String} [args.stop] The ending token we want to find + @param {Boolean} [args.rawContents] True to skip recursive processing + @param {Function} [args.emitter] The emitting function to transform the contents of the block into jsonML **/ replaceBlock: function(args) { @@ -327,7 +329,7 @@ Discourse.Dialect = { if (!m) { return; } - var startPos = block.indexOf(m[0]), + var startPos = args.start.lastIndex - m[0].length, leading, blockContents = [], result = [], @@ -351,14 +353,11 @@ Discourse.Dialect = { lineNumber++; - var blockClosed = false; - if (next.length > 0) { - for (var i=0; i= 0) { - blockClosed = true; - break; - } + for (var i=0; i= 0) { + blockClosed = true; + break; } } @@ -367,33 +366,52 @@ Discourse.Dialect = { return; } + var numOpen = 1; while (next.length > 0) { var b = next.shift(), blockLine = b.lineNumber, diff = ((typeof blockLine === "undefined") ? lineNumber : blockLine) - lineNumber, endFound = b.indexOf(args.stop), leadingContents = b.slice(0, endFound), - trailingContents = b.slice(endFound+args.stop.length); + trailingContents = b.slice(endFound+args.stop.length), + m2; - if (endFound >= 0) { blockClosed = true; } + if (endFound === -1) { + leadingContents = b; + } + + args.start.lastIndex = 0; + if (m2 = (args.start).exec(leadingContents)) { + numOpen++; + args.start.lastIndex -= m2[0].length - 1; + while (m2 = (args.start).exec(leadingContents)) { + numOpen++; + args.start.lastIndex -= m2[0].length - 1; + } + } + + if (endFound >= 0) { numOpen--; } for (var j=1; j= 0) { if (trailingContents) { next.unshift(MD.mk_block(trailingContents.replace(/^\s+/, ""))); } blockContents.push(leadingContents.replace(/\s+$/, "")); - break; + + if (numOpen === 0) { + break; + } + blockContents.push(args.stop); } else { blockContents.push(b); } } - var emitterResult = args.emitter.call(this, blockContents, m, dialect.options); if (emitterResult) { result.push(emitterResult); diff --git a/app/assets/javascripts/discourse/dialects/quote_dialect.js b/app/assets/javascripts/discourse/dialects/quote_dialect.js index f2d2a052b06..3b8f0003ef0 100644 --- a/app/assets/javascripts/discourse/dialects/quote_dialect.js +++ b/app/assets/javascripts/discourse/dialects/quote_dialect.js @@ -5,16 +5,16 @@ var esc = Handlebars.Utils.escapeExpression; Discourse.Dialect.replaceBlock({ - start: new RegExp("\\[quote=?([^\\[\\]]+)?\\]([\\s\\S]*)", "igm"), + start: new RegExp("\\[quote(=[^\\[\\]]+)?\\]([\\s\\S]*)", "igm"), stop: '[/quote]', emitter: function(blockContents, matches, options) { var params = {'class': 'quote'}, - username; + username = null; if (matches[1]) { - var paramsString = matches[1].replace(/\"/g, ''), - paramsSplit = paramsString.split(/\, */); + var paramsString = matches[1].replace(/^=|\"/g, ''), + paramsSplit = paramsString.split(/\,\s*/); username = paramsSplit[0]; @@ -38,25 +38,37 @@ Discourse.Dialect.replaceBlock({ avatarImg = options.lookupAvatar(username); } + while (blockContents.length && (typeof blockContents[0] === "string" || blockContents[0] instanceof String)) { + blockContents[0] = String(blockContents[0]).replace(/^\s+/, ''); + if (!blockContents[0].length) { + blockContents.shift(); + } else { + break; + } + } + var contents = ['blockquote']; if (blockContents.length) { var self = this; - if (blockContents && (typeof blockContents[0] === "string")) { - blockContents[0] = blockContents[0].replace(/^[\s]*/, ''); - } + var nextContents = blockContents.slice(1); + blockContents = this.processBlock(blockContents[0], nextContents).concat(nextContents); blockContents.forEach(function (bc) { - var processed = self.processInline(bc); - if (processed.length) { - contents.push(['p'].concat(processed)); + if (typeof bc === "string" || bc instanceof String) { + var processed = self.processInline(String(bc)); + if (processed.length) { + contents.push(['p'].concat(processed)); + } + } else { + contents.push(bc); } }); } // If there's no username just return a simple quote if (!username) { - return ['p', ['aside', params, contents ]]; + return ['p', ['aside', params, contents]]; } return ['p', ['aside', params, diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 2bb97f82d64..64415f0d494 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -686,7 +686,7 @@ blockquote { /* solo quotes */ } .quote { /* quotes with attribution */ - blockquote { + &>blockquote { margin-top: 0; padding-top: 0; p:first-of-type {margin:0;} @@ -708,6 +708,10 @@ blockquote { /* solo quotes */ background: darken(scale-color-diff(), 5%); border-left: 5px solid darken(scale-color-diff(), 12%); } + + aside.quote>blockquote, aside.quote>.title { + border-left: 0; + } } } diff --git a/test/javascripts/lib/bbcode_test.js b/test/javascripts/lib/bbcode_test.js index 3d335b2aa38..10f1d14aa61 100644 --- a/test/javascripts/lib/bbcode_test.js +++ b/test/javascripts/lib/bbcode_test.js @@ -99,7 +99,7 @@ test("quotes", function() { "", "it doesn't insert a new line for italics"); - format("[quote,script='a'>