<div class="messageText">
<ul class="messageQuoteItemList">
{foreach from=$message key=quoteID item=quote}
- <li data-quote-id="{@$quoteID}">
+ <li data-quote-id="{@$quoteID}" data-is-full-quote="{if $message->isFullQuote($quoteID)}true{else}false{/if}">
<span>
<input type="checkbox" value="1" id="quote_{@$quoteID}" class="jsCheckbox">
{if $supportPaste}<span class="icon icon16 fa-plus jsTooltip jsInsertQuote" title="{lang}wcf.message.quote.insertQuote{/lang}"></span>{/if}
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabMedia.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabMention.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabModal.js?v={@LAST_UPDATE_TIME}',
+ '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabParagraphize.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabQuote.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSize.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSmiley.js?v={@LAST_UPDATE_TIME}',
'WoltLabInlineCode',
'WoltLabLink',
'WoltLabModal',
+ 'WoltLabParagraphize',
'WoltLabQuote',
'WoltLabSize',
'WoltLabSmiley',
// already and we can safely add all icons
config.plugins.push('WoltLabButton');
+ var content = element.value;
+ element.value = '';
+
+ config.callbacks = config.callbacks || { };
+ config.callbacks.init = function() {
+ // slight delay to allow Redactor to initialize itself
+ window.setTimeout(function() {
+ $(element).redactor('code.set', content);
+ }, 10);
+ };
+
$(element).redactor(config);
});
});
return (this.utils.isCollapsed()) ? this.block.formatCollapsed(tag, attr, value, type) : this.block.formatUncollapsed(tag, attr, value, type);
}).bind(this);
+
+ var mpFormatCollapsed = this.block.formatCollapsed;
+ this.block.formatCollapsed = (function(tag, attr, value, type) {
+ var replaced = mpFormatCollapsed.call(this, tag, attr, value, type);
+
+ for (var i = 0, length = replaced.length; i < length; i++) {
+ this.WoltLabBlock._paragraphize(replaced[i]);
+ }
+
+ return replaced;
+ }).bind(this);
+
+ var mpFormatUncollapsed = this.block.formatUncollapsed;
+ this.block.formatUncollapsed = (function(tag, attr, value, type) {
+ var replaced = mpFormatUncollapsed.call(this, tag, attr, value, type);
+
+ var block, firstBlock = null;
+ for (var i = 0, length = replaced.length; i < length; i++) {
+ block = replaced[i][0];
+
+ this.WoltLabBlock._paragraphize(block);
+
+ if (i === 0) {
+ firstBlock = block;
+ }
+ else {
+ while (block.childNodes.length) {
+ firstBlock.appendChild(block.childNodes[0]);
+ }
+
+ elRemove(block);
+ }
+ }
+
+ return $(firstBlock);
+ }).bind(this);
},
register: function(tag, arrowKeySupport) {
if (arrowKeySupport) {
this.WoltLabKeydown.register(tag);
}
+ },
+
+ _paragraphize: function (block) {
+ if (['p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'figure'].indexOf(block.nodeName) !== -1) {
+ // do not paragraphize these blocks
+ return;
+ }
+
+ var paragraph = elCreate('p');
+ while (block.childNodes.length) {
+ paragraph.appendChild(block.childNodes[0]);
+ }
+
+ block.appendChild(paragraph);
}
}
};
}
}
}).bind(this);
+
+ var mpOnEnter = this.keydown.onEnter;
+ this.keydown.onEnter = (function(e) {
+ var isBlockquote = this.keydown.blockquote;
+ if (isBlockquote) this.keydown.blockquote = false;
+
+ mpOnEnter.call(this, e);
+
+ if (isBlockquote) this.keydown.blockquote = isBlockquote;
+ }).bind(this);
},
register: function (tag) {
--- /dev/null
+$.Redactor.prototype.WoltLabParagraphize = function() {
+ "use strict";
+
+ return {
+ init: function () {
+ this.paragraphize.getSafes = (function (html) {
+ var $div = $('<div />').append(html);
+
+ // WoltLab modification: do not remove <p> inside quotes
+ // remove paragraphs in blockquotes
+ /*$div.find('blockquote p').replaceWith(function()
+ {
+ return $(this).append('<br />').contents();
+ });*/
+
+ $div.find(this.opts.paragraphizeBlocks.join(', ')).each($.proxy(function(i,s)
+ {
+ this.paragraphize.z++;
+ this.paragraphize.safes[this.paragraphize.z] = s.outerHTML;
+
+ return $(s).replaceWith('\n#####replace' + this.paragraphize.z + '#####\n\n');
+
+
+ }, this));
+
+
+ return $div.html();
+ }).bind(this)
+ }
+ };
+};
init: function() {
var button = this.button.add('woltlabQuote', '');
+ this.WoltLabBlock.register('woltlab-quote', true);
+ this.opts.replaceTags.blockquote = 'woltlab-quote';
+
+ // support for active button marking
+ this.opts.activeButtonsStates['woltlab-quote'] = 'woltlabQuote';
+
require(['WoltLabSuite/Core/Ui/Redactor/Quote'], (function (UiRedactorQuote) {
new UiRedactorQuote(this, button);
}).bind(this));
return {
init: function() {
+ // register custom block element
+ this.WoltLabBlock.register('woltlab-spoiler', true);
+
+ // support for active button marking
+ this.opts.activeButtonsStates['woltlab-spoiler'] = 'woltlabSpoiler';
+
require(['WoltLabSuite/Core/Ui/Redactor/Spoiler'], (function (UiRedactorSpoiler) {
new UiRedactorSpoiler(this);
}).bind(this));
var message = listItem.parents('.message:eq(0)');
var author = message.data('username');
var link = message.data('link');
- var isText = listItem.find('.jsQuote').length === 0;
+ var isText = elData(listItem[0], 'is-full-quote');
WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'insertQuote_' + (this._editorIdAlternative ? this._editorIdAlternative : this._editorId), {
author: author,
* @param {jQuery} button toolbar button
*/
init: function(editor, button) {
- this._blockquote = null;
+ this._quote = null;
this._editor = editor;
this._elementId = this._editor.$element[0].id;
this._editor.button.addCallback(button, this._click.bind(this));
- // support for active button marking
- this._editor.opts.activeButtonsStates.blockquote = 'woltlabQuote';
-
// static bind to ensure that removing works
this._callbackEdit = this._edit.bind(this);
block = this._editor.selection.block();
}
- if (block.nodeName !== 'P') {
- var redactor = this._editor.core.editor()[0];
-
- // find parent before Redactor
- while (block.parentNode !== redactor) {
- block = block.parentNode;
- }
-
- // caret.after() requires a following element
- var next = this._editor.caret.next(block);
- if (next === undefined || next.nodeName !== 'P') {
- var p = elCreate('p');
- p.textContent = '\u200B';
-
- DomUtil.insertAfter(p, block);
- }
-
- this._editor.caret.after(block);
+ var redactor = this._editor.core.editor()[0];
+ while (block.parentNode && block.parentNode !== redactor) {
+ block = block.parentNode;
}
- var content = '';
- if (data.isText) content = this._editor.marker.html();
- else content = data.content;
+ this._editor.caret.after(block);
var quoteId = Core.getUuid();
- this._editor.insert.html('<blockquote id="' + quoteId + '">' + content + '</blockquote>');
+ this._editor.insert.html('<woltlab-quote id="' + quoteId + '"></woltlab-quote>');
var quote = elById(quoteId);
elData(quote, 'author', data.author);
elData(quote, 'link', data.link);
+ this._editor.selection.restore();
+ var content = data.content;
+ console.debug(data);
if (data.isText) {
- this._editor.selection.restore();
- this._editor.insert.text(data.content);
+ content = StringUtil.escapeHTML(content);
+ content = '<p>' + content + '</p>';
+ content = content.replace(/\n\n/g, '</p><p>');
+ content = content.replace(/\n/g, '<br>');
}
+ // bypass the editor as `insert.html()` doesn't like us
+ quote.innerHTML = content;
+
quote.removeAttribute('id');
this._editor.caret.after(quote);
* @protected
*/
_click: function() {
- this._editor.button.toggle({}, 'blockquote', 'func', 'block.format');
+ this._editor.button.toggle({}, 'woltlab-quote', 'func', 'block.format');
- var blockquote = this._editor.selection.block();
- if (blockquote && blockquote.nodeName === 'BLOCKQUOTE') {
- this._setTitle(blockquote);
+ var quote = this._editor.selection.block();
+ if (quote && quote.nodeName === 'WOLTLAB-QUOTE') {
+ this._setTitle(quote);
- blockquote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+ quote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
}
},
* @protected
*/
_observeLoad: function() {
- elBySelAll('blockquote', this._editor.$editor[0], (function(blockquote) {
- blockquote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
- this._setTitle(blockquote);
+ elBySelAll('woltlab-quote', this._editor.$editor[0], (function(quote) {
+ quote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+ this._setTitle(quote);
}).bind(this));
},
* @protected
*/
_edit: function(event) {
- var blockquote = event.currentTarget;
+ var quote = event.currentTarget;
if (_headerHeight === 0) {
- _headerHeight = ~~window.getComputedStyle(blockquote).paddingTop.replace(/px$/, '');
+ _headerHeight = ~~window.getComputedStyle(quote).paddingTop.replace(/px$/, '');
- var styles = window.getComputedStyle(blockquote, '::before');
+ var styles = window.getComputedStyle(quote, '::before');
_headerHeight += ~~styles.paddingTop.replace(/px$/, '');
_headerHeight += ~~styles.height.replace(/px$/, '');
_headerHeight += ~~styles.paddingBottom.replace(/px$/, '');
}
// check if the click hit the header
- var offset = DomUtil.offset(blockquote);
+ var offset = DomUtil.offset(quote);
if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
event.preventDefault();
- this._blockquote = blockquote;
+ this._quote = quote;
UiDialog.open(this);
}
}
// set author
- elData(this._blockquote, 'author', elById(id + '-author').value);
+ elData(this._quote, 'author', elById(id + '-author').value);
// set url
- elData(this._blockquote, 'url', url);
+ elData(this._quote, 'url', url);
- this._setTitle(this._blockquote);
- this._editor.caret.after(this._blockquote);
+ this._setTitle(this._quote);
+ this._editor.caret.after(this._quote);
UiDialog.close(this);
},
/**
* Sets or updates the quote's header title.
*
- * @param {Element} blockquote quote element
+ * @param {Element} quote quote element
* @protected
*/
- _setTitle: function(blockquote) {
+ _setTitle: function(quote) {
var title = Language.get('wcf.editor.quote.title', {
- author: elData(blockquote, 'author'),
- url: elData(blockquote, 'url')
+ author: elData(quote, 'author'),
+ url: elData(quote, 'url')
});
- if (elData(blockquote, 'title') !== title) {
- elData(blockquote, 'title', title);
+ if (elData(quote, 'title') !== title) {
+ elData(quote, 'title', title);
}
},
}).bind(this),
onShow: (function() {
- elById(idAuthor).value = elData(this._blockquote, 'author');
- elById(idUrl).value = elData(this._blockquote, 'url');
+ elById(idAuthor).value = elData(this._quote, 'author');
+ elById(idUrl).value = elData(this._quote, 'url');
}).bind(this),
title: Language.get('wcf.editor.quote.edit')
EventHandler.add('com.woltlab.wcf.redactor2', 'bbcode_spoiler_' + this._elementId, this._bbcodeSpoiler.bind(this));
EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
- // register custom block element
- this._editor.WoltLabBlock.register('woltlab-spoiler', true);
-
- // support for active button marking
- this._editor.opts.activeButtonsStates['woltlab-spoiler'] = 'woltlabSpoiler';
-
// static bind to ensure that removing works
this._callbackEdit = this._edit.bind(this);
protected function setAttributeDefinitions(\HTMLPurifier_Config $config) {
$definition = $config->getHTMLDefinition(true);
- // quotes
- $definition->addAttribute('blockquote', 'data-author', 'Text');
- $definition->addAttribute('blockquote', 'data-link', 'URI');
-
// code
$definition->addAttribute('pre', 'data-file', 'Text');
$definition->addAttribute('pre', 'data-line', 'Number');
$definition->addAttribute('img', 'data-media-id', 'Number');
$definition->addAttribute('img', 'data-media-size', new \HTMLPurifier_AttrDef_Enum(['small', 'medium', 'large', 'original']));
+ // quote
+ $definition->addElement('woltlab-quote', 'Block', 'Flow', '', [
+ 'data-author' => 'Text',
+ 'data-link' => 'URI'
+ ]);
+
// spoiler
$definition->addElement('woltlab-spoiler', 'Block', 'Flow', '', [
'data-label' => 'Text'
+++ /dev/null
-<?php
-namespace wcf\system\html\output\node;
-use wcf\system\application\ApplicationHandler;
-use wcf\system\html\node\AbstractHtmlNodeProcessor;
-use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
-use wcf\system\request\RouteHandler;
-use wcf\system\WCF;
-use wcf\util\DOMUtil;
-use wcf\util\StringUtil;
-
-/**
- * Processes quotes.
- *
- * @author Alexander Ebert
- * @copyright 2001-2016 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\System\Html\Output\Node
- * @since 3.0
- */
-class HtmlOutputNodeBlockquote extends AbstractHtmlOutputNode {
- /**
- * @inheritDoc
- */
- protected $tagName = 'blockquote';
-
- /**
- * @inheritDoc
- */
- public function process(array $elements, AbstractHtmlNodeProcessor $htmlNodeProcessor) {
- /** @var \DOMElement $element */
- foreach ($elements as $element) {
- switch ($this->outputType) {
- case 'text/html':
- $nodeIdentifier = StringUtil::getRandomID();
- $htmlNodeProcessor->addNodeData($this, $nodeIdentifier, [
- 'author' => $element->getAttribute('data-author'),
- 'url' => $element->getAttribute('data-link')
- ]);
-
- $htmlNodeProcessor->renameTag($element, 'wcfNode-' . $nodeIdentifier);
- break;
-
- case 'text/simplified-html':
- case 'text/plain':
- // check if this quote is within another
- if (DOMUtil::hasParent($element, 'blockquote')) {
- DOMUtil::removeNode($element);
- }
- else {
- $htmlNodeProcessor->replaceElementWithText(
- $element,
- WCF::getLanguage()->getDynamicVariable('wcf.bbcode.quote.simplified', ['cite' => $element->getAttribute('data-author')]),
- true
- );
- }
- break;
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- public function replaceTag(array $data) {
- $externalQuoteLink = (!empty($data['url'])) ? !ApplicationHandler::getInstance()->isInternalURL($data['url']) : false;
- if (!$externalQuoteLink) {
- $data['url'] = preg_replace('~^https://~', RouteHandler::getProtocol(), $data['url']);
- }
-
- $quoteAuthorObject = null;
- if ($data['author'] && !$externalQuoteLink) {
- $quoteAuthorLC = mb_strtolower(StringUtil::decodeHTML($data['author']));
- foreach (MessageEmbeddedObjectManager::getInstance()->getObjects('com.woltlab.wcf.quote') as $user) {
- if (mb_strtolower($user->username) == $quoteAuthorLC) {
- $quoteAuthorObject = $user;
- break;
- }
- }
- }
-
- WCF::getTPL()->assign([
- 'quoteLink' => $data['url'],
- 'quoteAuthor' => $data['author'],
- 'quoteAuthorObject' => $quoteAuthorObject,
- 'isExternalQuoteLink' => $externalQuoteLink
- ]);
- return WCF::getTPL()->fetch('quoteMetaCode');
- }
-}
--- /dev/null
+<?php
+namespace wcf\system\html\output\node;
+use wcf\system\application\ApplicationHandler;
+use wcf\system\html\node\AbstractHtmlNodeProcessor;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
+use wcf\system\request\RouteHandler;
+use wcf\system\WCF;
+use wcf\util\DOMUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Processes quotes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Html\Output\Node
+ * @since 3.0
+ */
+class HtmlOutputNodeWoltlabQuote extends AbstractHtmlOutputNode {
+ /**
+ * @inheritDoc
+ */
+ protected $tagName = 'woltlab-quote';
+
+ /**
+ * @inheritDoc
+ */
+ public function process(array $elements, AbstractHtmlNodeProcessor $htmlNodeProcessor) {
+ /** @var \DOMElement $element */
+ foreach ($elements as $element) {
+ switch ($this->outputType) {
+ case 'text/html':
+ $nodeIdentifier = StringUtil::getRandomID();
+ $htmlNodeProcessor->addNodeData($this, $nodeIdentifier, [
+ 'author' => $element->getAttribute('data-author'),
+ 'url' => $element->getAttribute('data-link')
+ ]);
+
+ $htmlNodeProcessor->renameTag($element, 'wcfNode-' . $nodeIdentifier);
+ break;
+
+ case 'text/simplified-html':
+ case 'text/plain':
+ // check if this quote is within another
+ if (DOMUtil::hasParent($element, 'woltlab-quote')) {
+ DOMUtil::removeNode($element);
+ }
+ else {
+ $htmlNodeProcessor->replaceElementWithText(
+ $element,
+ WCF::getLanguage()->getDynamicVariable('wcf.bbcode.quote.simplified', ['cite' => $element->getAttribute('data-author')]),
+ true
+ );
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function replaceTag(array $data) {
+ $externalQuoteLink = (!empty($data['url'])) ? !ApplicationHandler::getInstance()->isInternalURL($data['url']) : false;
+ if (!$externalQuoteLink) {
+ $data['url'] = preg_replace('~^https://~', RouteHandler::getProtocol(), $data['url']);
+ }
+
+ $quoteAuthorObject = null;
+ if ($data['author'] && !$externalQuoteLink) {
+ $quoteAuthorLC = mb_strtolower(StringUtil::decodeHTML($data['author']));
+ foreach (MessageEmbeddedObjectManager::getInstance()->getObjects('com.woltlab.wcf.quote') as $user) {
+ if (mb_strtolower($user->username) == $quoteAuthorLC) {
+ $quoteAuthorObject = $user;
+ break;
+ }
+ }
+ }
+
+ WCF::getTPL()->assign([
+ 'quoteLink' => $data['url'],
+ 'quoteAuthor' => $data['author'],
+ 'quoteAuthorObject' => $quoteAuthorObject,
+ 'isExternalQuoteLink' => $externalQuoteLink
+ ]);
+ return WCF::getTPL()->fetch('quoteMetaCode');
+ }
+}
-.redactor-editor blockquote,
+woltlab-quote,
.quoteBox {
background-color: $wcfContentBackground;
box-shadow: 0 0 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);
+ display: block;
font-style: italic;
margin: 20px 0;
padding: 20px;
}
}
-.redactor-editor blockquote::before {
+woltlab-quote::before {
content: attr(data-title);
cursor: pointer;
display: block;