From 6166782d1284579059891dc41ba21bc82e4f9d58 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 5 Aug 2014 13:49:17 +0200 Subject: [PATCH] Improved handling of quotes, fixed DOM issues related to linebreaks --- .../js/3rdParty/redactor/plugins/wbbcode.js | 201 +++++++++++------- .../3rdParty/redactor/plugins/wmonkeypatch.js | 11 +- 2 files changed, 137 insertions(+), 75 deletions(-) diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js index 1a80cd6170..3dc153496f 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js @@ -171,15 +171,15 @@ RedactorPlugins.wbbcode = { html = html.replace(/\r?\n/g, ''); // convert paragraphs into single lines - var $parts = html.split(/(<\/?p>)/); + var $parts = html.split(/(<\/?(?:div|p)>)/); var $tmp = ''; var $buffer = ''; for (var $i = 0; $i < $parts.length; $i++) { var $part = $parts[$i]; - if ($part == '

') { + if ($part == '

' || $part == '

') { continue; } - else if ($part == '

') { + else if ($part == '

' || $part == '
') { $buffer = $.trim($buffer); if ($buffer != '@@@wcf_empty_line@@@') { $buffer += "\n"; @@ -189,7 +189,7 @@ RedactorPlugins.wbbcode = { $buffer = ''; } else { - if ($i == 0) { + if ($i == 0 || $i + 1 == $parts.length) { $tmp += $part; } else { @@ -197,6 +197,12 @@ RedactorPlugins.wbbcode = { } } } + + if ($buffer) { + $tmp += $buffer; + $buffer = ''; + } + html = $tmp; html = html.replace(/@@@wcf_empty_line@@@/g, '\n'); @@ -210,6 +216,24 @@ RedactorPlugins.wbbcode = { html = html.replace(/
/g, ''); html = html.replace(/ /gi, " "); + // [quote] + html = html.replace(/
\n?]+>\n?
[\s\S]*?<\/header>/gi, function(match, link, author, innerContent) { + var $quote; + + if (link) { + $quote = "[quote='" + author + "','" + link + "']"; + } + else if (author) { + $quote = "[quote='" + author + "']"; + } + else { + $quote = "[quote]"; + } + + return $quote; + }); + html = html.replace(/(?:\n*)<\/blockquote>/gi, '[/quote]\n'); + // [email] html = html.replace(/]*?href=(["'])mailto:(.+?)\1.*?>([\s\S]+?)<\/a>/gi, '[email=$2]$3[/email]'); @@ -255,20 +279,6 @@ RedactorPlugins.wbbcode = { html = html.replace(/]*?src=(["'])([^"']+?)\1 style="float: (left|right)[^"]*".*?>/gi, "[img='$2',$3][/img]"); html = html.replace(/]*?src=(["'])([^"']+?)\1.*?>/gi, '[img]$2[/img]'); - // [quote] - html = html.replace(/
/gi, function(match, link, author) { - if (link) { - return "[quote='" + author + "','" + link + "']"; - } - else if (author) { - return "[quote='" + author + "']"; - } - - return "[quote]"; - }); - html = html.replace(/<\/blockquote>/gi, '[/quote]'); - html = html.replace(/
.*?<\/header>/gi, ''); - // handle [color], [size], [font] and [tt] var $components = html.split(/(<\/?span[^>]*>)/); @@ -431,18 +441,6 @@ RedactorPlugins.wbbcode = { }); } - // trim leading tabs - var $tmp = html.split("\n"); - for (var $i = 0, $length = $tmp.length; $i < $length; $i++) { - $tmp[$i] = $tmp[$i].replace(/^\s*/, ''); - } - html = $tmp.join("\n"); - - // trim whitespaces within quote tags - html = html.replace(/\[quote([^\]]+)?\](.*?)\[\/quote\]/, function(match, attributes, content) { - return '[quote' + (attributes || '') + ']' + $.trim(content) + '[/quote]'; - }); - WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'afterConvertFromHtml', { html: html }); // insert codes @@ -594,7 +592,19 @@ RedactorPlugins.wbbcode = { data = data.replace(/(javascript):/gi, '$1:'); // unify line breaks - data = data.replace(/(\r|\r\n)/, "\n"); + data = data.replace(/(\r|\r\n)/g, "\n"); + + // extract [quote] bbcodes to prevent line break handling below + var $cachedQuotes = { }; + data = data.replace(/\[quote.*?\][\S\s]*?\[\/quote\]/gi, function(match) { + var $key = match.hashCode(); + $cachedQuotes[$key] = match.replace(/\$/g, '$$$$'); + return '@@' + $key + '@@'; + }); + + // add newlines before and after [quote] tags + data = data.replace(/(\[quote.*?\])/gi, '$1\n'); + data = data.replace(/(\[\/quote\])/gi, '\n$1'); // convert line breaks into

or empty lines to


var $tmp = data.split("\n"); @@ -628,50 +638,69 @@ RedactorPlugins.wbbcode = { // preserve leading whitespaces in [code] tags data = data.replace(/\[code\][\S\s]*?\[\/code\]/, '
$&
'); - // [quote] - var $unquoteString = function(quotedString) { - return quotedString.replace(/^['"]/, '').replace(/['"]$/, ''); - }; - - data = data.replace(/\[quote([^\]]+)?\]/gi, $.proxy(function(match, attributes) { - var $author = ''; - var $link = ''; + // insert quotes + if ($.getLength($cachedQuotes)) { + // [quote] + var $unquoteString = function(quotedString) { + return quotedString.replace(/^['"]/, '').replace(/['"]$/, ''); + }; - if (attributes) { - attributes = attributes.substr(1); - attributes = attributes.split(','); - - switch (attributes.length) { - case 1: - $author = attributes[0]; - break; + var self = this; + var $transformQuote = function(quote) { + return quote.replace(/\[quote([^\]]+)?\]([\S\s]*)\[\/quote\]?/gi, $.proxy(function(match, attributes, innerContent) { + var $author = ''; + var $link = ''; - case 2: - $author = attributes[0]; - $link = attributes[1]; - break; - } - - $author = WCF.String.escapeHTML($unquoteString($.trim($author))); - $link = WCF.String.escapeHTML($unquoteString($.trim($link))); - } - - var $quote = '
' - + '
' - + '
' - + '

' - + this._buildQuoteHeader($author, $link) - + '

' - + '
' - + '
' - + '
'; + if (attributes) { + attributes = attributes.substr(1); + attributes = attributes.split(','); + + switch (attributes.length) { + case 1: + $author = attributes[0]; + break; + + case 2: + $author = attributes[0]; + $link = attributes[1]; + break; + } + + $author = WCF.String.escapeHTML($unquoteString($.trim($author))); + $link = WCF.String.escapeHTML($unquoteString($.trim($link))); + } + + var $quote = '
' + + '
' + + '
' + + '

' + + self._buildQuoteHeader($author, $link) + + '

' + + '' + + '
'; + + var $lines = innerContent.split('\n'); + var $tmp = ''; + for (var $i = 0; $i < $lines.length; $i++) { + $tmp += '
' + $lines[$i] + '
'; + } + + if (!$tmp) { + $tmp = '
' + this.opts.invisibleSpace + '
'; + } + + $quote += $tmp; + $quote += '
'; + + return $quote; + }, this)); + }; - return $quote; - }, this)); - data = data.replace(/\[\/quote\]/gi, '
'); - - data = data.replace(/

<\/p>/, '
'); + for (var $key in $cachedQuotes) { + var $regex = new RegExp('@@' + $key + '@@', 'g'); + data = data.replace($regex, $transformQuote($cachedQuotes[$key])); + } + } WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'afterConvertToHtml', { data: data }); @@ -798,7 +827,7 @@ RedactorPlugins.wbbcode = { }, /** - * Handles down key for quote boxes. + * Handles up/down key for quote boxes. * * @param object event * @return boolean @@ -814,6 +843,30 @@ RedactorPlugins.wbbcode = { return false; } + else if (event.which === $.ui.keyCode.UP) { + var $parent = this.getParent(); + var $quote = ($parent) ? $($parent).closest('blockquote.quoteBox', this.$editor.get()[0]) : { length: 0 }; + + if ($parent && $quote.length) { + var $previousElement = $quote.prev(); + if ($previousElement.length === 0) { + var $node = $(this.opts.emptyHtml); + $node.insertBefore($quote); + this.selectionStart($node); + } + else { + if ($previousElement[0].tagName === 'BLOCKQUOTE') { + // set focus to quote text rather than the element itself + this.selectionEnd($previousElement.find('> div > div')); + } + else { + this.selectionEnd($previousElement); + } + } + } + + return false; + } return true; }, @@ -916,7 +969,7 @@ RedactorPlugins.wbbcode = { if (this.inWysiwygMode()) { var $html = '
' + '
' - + '
' + + '
' + '

' + this._buildQuoteHeader(author, link) + '

' diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js index d3bd555bd7..8c72e748db 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js @@ -491,5 +491,14 @@ RedactorPlugins.wmonkeypatch = { return false; } - } + }, + + /** + * Overwrites $.Redactor.cleanGetTabs() to prevent HTML indentation. + * + * @see $.Redactor.cleanGetTabs() + */ + cleanGetTabs: function() { + return ''; + }, }; -- 2.20.1