From: Alexander Ebert Date: Wed, 15 Jun 2016 14:50:07 +0000 (+0200) Subject: Streamlined quote handling X-Git-Tag: 3.0.0_Beta_1~1432 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=61328173487e277770ec7fe3c926757fa27bcc1b;p=GitHub%2FWoltLab%2FWCF.git Streamlined quote handling --- diff --git a/com.woltlab.wcf/templates/wysiwyg.tpl b/com.woltlab.wcf/templates/wysiwyg.tpl index 94e517484e..065a7f06c3 100644 --- a/com.woltlab.wcf/templates/wysiwyg.tpl +++ b/com.woltlab.wcf/templates/wysiwyg.tpl @@ -49,6 +49,7 @@ 'wcf.editor.code.line': '{lang}wcf.editor.code.line{/lang}', 'wcf.editor.code.line.description': '{lang}wcf.editor.code.line.description{/lang}', 'wcf.editor.code.title': '{lang __literal=true}wcf.editor.code.title{/lang}', + 'wcf.editor.image.edit': '{lang}wcf.editor.image.edit{/lang}', 'wcf.editor.image.insert': '{lang}wcf.editor.image.insert{/lang}', 'wcf.editor.image.link': '{lang}wcf.editor.image.link{/lang}', @@ -57,7 +58,13 @@ 'wcf.editor.image.float.left': '{lang}wcf.editor.image.float.left{/lang}', 'wcf.editor.image.float.right': '{lang}wcf.editor.image.float.right{/lang}', 'wcf.editor.image.source': '{lang}wcf.editor.image.source{/lang}', - 'wcf.editor.image.source.error.invalid': '{lang}wcf.editor.image.source.error.invalid{/lang}' + 'wcf.editor.image.source.error.invalid': '{lang}wcf.editor.image.source.error.invalid{/lang}', + + 'wcf.editor.quote.author': '{lang}wcf.editor.quote.author{/lang}', + 'wcf.editor.quote.edit': '{lang}wcf.editor.quote.edit{/lang}', + 'wcf.editor.quote.title': '{lang __literal=true}wcf.editor.quote.title{/lang}', + 'wcf.editor.quote.url': '{lang}wcf.editor.quote.url{/lang}', + 'wcf.editor.quote.url.description': '{lang}wcf.editor.quote.url.description{/lang}' }); var buttons = [], buttonOptions = [], customButtons = []; diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabQuote.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabQuote.js index bf5d08fb66..79b71c469d 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabQuote.js +++ b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabQuote.js @@ -3,31 +3,10 @@ $.Redactor.prototype.WoltLabQuote = function() { return { init: function() { - // TODO: this should be somewhere else var button = this.button.add('woltlabQuote', ''); - this.button.addCallback(button, this.WoltLabQuote.insert); - require(['WoltLab/WCF/Ui/Redactor/Quote'], (function(UiRedactorQuote) { - UiRedactorQuote.initEditor(this.$element[0].id, this.$editor[0]); - }).bind(this)); - }, - - insert: function() { - require(['Dom/Traverse', 'WoltLab/WCF/Ui/Redactor/Quote'], (function(DomTraverse, UiRedactorQuote) { - var current = this.selection.current(); - if (current) { - if (current.nodeType === Node.TEXT_NODE) current = current.parentNode; - - if (current.nodeName === 'BLOCKQUOTE' || DomTraverse.parentByTag(current, 'BLOCKQUOTE', this.$editor[0])) { - return; - } - } - - UiRedactorQuote.insert((function(element) { - element.innerHTML = this.opts.invisibleSpace + this.selection.markerHtml(); - - this.insert.html(element.outerHTML); - }).bind(this)); + require(['WoltLab/WCF/Ui/Redactor/Quote'], (function (UiRedactorQuote) { + new UiRedactorQuote(this, button); }).bind(this)); } }; diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Code.js b/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Code.js index 45e04ec966..b92ec60e79 100644 --- a/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Code.js +++ b/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Code.js @@ -1,5 +1,5 @@ /** - * Provides helper functions to work with DOM nodes. + * Manages code blocks. * * @author Alexander Ebert * @copyright 2001-2016 WoltLab GmbH @@ -53,22 +53,9 @@ define(['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Di var pre = this._editor.selection.block(); if (pre && pre.nodeName === 'PRE') { - if (pre.textContent === '') { - pre.textContent = '\u200B'; - } - this._setTitle(pre); pre.addEventListener(WCF_CLICK_EVENT, this._callbackEdit); - - // there must be some kind of element after the
-				if (pre.nextElementSibling === null) {
-					var p = elCreate('p');
-					p.textContent = '\u200B';
-					pre.parentNode.appendChild(p);
-				}
-				
-				this._editor.caret.after(pre);
 			}
 		},
 		
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Quote.js b/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Quote.js
index f88a4272a9..37e979686c 100644
--- a/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Quote.js
+++ b/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Quote.js
@@ -1,202 +1,193 @@
 /**
- * Manages insertation and editing of quotes.
- * 
- * @author	Alexander Ebert
- * @copyright	2001-2015 WoltLab GmbH
- * @license	GNU Lesser General Public License 
- * @module	WoltLab/WCF/Ui/Redactor/Quote
+ * Manages quotes.
+ *
+ * @author      Alexander Ebert
+ * @copyright   2001-2016 WoltLab GmbH
+ * @license     GNU Lesser General Public License 
+ * @module      WoltLab/WCF/Ui/Redactor/Quote
  */
-define(['EventHandler', 'Language', 'Dom/Util', 'Ui/Dialog'], function(EventHandler, Language, DomUtil, UiDialog) {
+define(['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog) {
 	"use strict";
 	
-	var _callbackEdit = null;
-	var _element = null;
-	var _insertCallback = null;
-	var _quotePaddingTop = 0;
-	var _titleHeight = 0;
-	var _wysiwygQuoteButton = null;
-	var _wysiwygQuoteTitle = null;
-	var _wysiwygQuoteUrl = null;
+	var _headerHeight = 0;
 	
-	return {
+	/**
+	 * @param       {Object}        editor  editor instance
+	 * @param       {jQuery}        button  toolbar button
+	 * @constructor
+	 */
+	function UiRedactorQuote(editor, button) { this.init(editor, button); }
+	UiRedactorQuote.prototype = {
 		/**
-		 * Registers an editor instance.
+		 * Initializes the quote management.
 		 * 
-		 * @param       {string}        editorId        textarea identifier
-		 * @param       {object}        editor          editor element
+		 * @param       {Object}        editor  editor instance
+		 * @param       {jQuery}        button  toolbar button
 		 */
-		initEditor: function(editorId, editor) {
-			EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + editorId, (function(data) {
-				this.observeAll(data.editor);
-			}).bind(this));
+		init: function(editor, button) {
+			this._blockquote = null;
+			this._editor = editor;
+			this._elementId = this._editor.$element[0].id;
 			
-			this.observeAll(editor);
-		},
-		
-		/**
-		 * Opens a dialog to insert a quote at caret position.
-		 * 
-		 * @param       {function}      callback        callback invoked with the 
element as parameter - */ - insert: function(callback) { - _insertCallback = callback; + EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this)); - UiDialog.open(this); - UiDialog.setTitle(this, Language.get('wcf.wysiwyg.quote.insert')); + this._editor.button.addCallback(button, this._click.bind(this)); - _wysiwygQuoteButton.textContent = Language.get('wcf.global.button.submit'); - _wysiwygQuoteTitle.value = ''; - _wysiwygQuoteUrl.value = ''; + // support for active button marking + this._editor.opts.activeButtonsStates.blockquote = 'woltlabQuote'; + + // static bind to ensure that removing works + this._callbackEdit = this._edit.bind(this); + + // bind listeners on init + this._observeLoad(); }, /** - * Edits a
element. + * Toggles the quote block on button click. * - * @param {Event?} event event object - * @param {Element=} element
element + * @protected */ - edit: function(event, element) { - if (event instanceof Event) { - element = event.currentTarget; - } - - if (_titleHeight === 0) { - var styles = window.getComputedStyle(element, '::before'); - _titleHeight = DomUtil.styleAsInt(styles, 'height'); - - styles = window.getComputedStyle(element); - _quotePaddingTop = DomUtil.styleAsInt(styles, 'padding-top'); - } + _click: function() { + this._editor.button.toggle({}, 'blockquote', 'func', 'block.format'); - if (event instanceof Event) { - // check if click occured within the ::before pseudo element - var rect = DomUtil.offset(element); - if ((event.clientY + window.scrollY) > (rect.top + _quotePaddingTop + _titleHeight)) { - return; - } + var blockquote = this._editor.selection.block(); + if (blockquote && blockquote.nodeName === 'BLOCKQUOTE') { + this._setTitle(blockquote); - event.preventDefault(); - } - - _element = element; - - UiDialog.open(this); - UiDialog.setTitle(this, Language.get('wcf.wysiwyg.quote.edit')); - - // set values - _wysiwygQuoteButton.textContent = Language.get('wcf.global.button.save'); - _wysiwygQuoteTitle.value = elData(_element, 'quote-title'); - _wysiwygQuoteUrl.value = elData(_element, 'quote-url'); - }, - - /** - * Observes all
elements for clicks on the editable headline - * @param {Element} editorElement editor element - */ - observeAll: function(editorElement) { - var elements = elByTag('BLOCKQUOTE', editorElement); - for (var i = 0, length = elements.length; i < length; i++) { - this._observe(elements[i], true); + blockquote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit); } }, /** - * Observes clicks on a
element and updates the headline. + * Binds event listeners and sets quote title on both editor + * initialization and when switching back from code view. * - * @param {Element} element
element - * @param {boolean} updateHeader update quote header + * @protected */ - _observe: function(element, updateHeader) { - if (_callbackEdit === null) _callbackEdit = this.edit.bind(this); + _observeLoad: function() { + this._editor.events.stopDetectChanges(); - element.addEventListener(WCF_CLICK_EVENT, _callbackEdit); + elBySelAll('blockquote', this._editor.$editor[0], (function(blockquote) { + blockquote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit); + this._setTitle(blockquote); + }).bind(this)); - if (updateHeader) this._updateHeader(element); + this._editor.events.startDetectChanges(); }, /** - * Updates the headline of target
element. + * Opens the dialog overlay to edit the quote's properties. * - * @param {Element} element
element + * @param {Event} event event object + * @protected */ - _updateHeader: function(element) { - var value = Language.get('wcf.wysiwyg.quote.header', { - title: elData(element, 'quote-title') || elData(element, 'quote-url') || '' - }); + _edit: function(event) { + var blockquote = event.currentTarget; + + if (_headerHeight === 0) { + _headerHeight = ~~window.getComputedStyle(blockquote).paddingTop.replace(/px$/, ''); + + var styles = window.getComputedStyle(blockquote, '::before'); + _headerHeight += ~~styles.paddingTop.replace(/px$/, ''); + _headerHeight += ~~styles.height.replace(/px$/, ''); + _headerHeight += ~~styles.paddingBottom.replace(/px$/, ''); + } - if (elData(element, 'quote-header') !== value) { - elData(element, 'quote-header', value); + // check if the click hit the header + var offset = DomUtil.offset(blockquote); + if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) { + event.preventDefault(); + + this._blockquote = blockquote; + + UiDialog.open(this); } }, /** - * Adds or edits a
element on dialog submit. + * Saves the changes to the quote's properties. + * + * @param {Event} event event object + * @protected */ - _dialogSubmit: function() { - if (_insertCallback !== null) { - // insert a new
element - var element = elCreate('blockquote'); - element.className = 'quoteBox'; - element.id = 'quote-' + DomUtil.getUniqueId(); - - _insertCallback(element); - - _element = elById(element.id); - _element.id = ''; - - this._observe(_element, false); - } + _save: function(event) { + event.preventDefault(); - // edit an existing
element - elData(_element, 'quote-title', _wysiwygQuoteTitle.value.trim()); - elData(_element, 'quote-url', _wysiwygQuoteUrl.value.trim()); + this._editor.events.stopDetectChanges(); - this._updateHeader(_element); + var id = 'redactor-quote-' + this._elementId; - UiDialog.close(this); - }, - - _dialogOnSetup: function() { - _wysiwygQuoteTitle = elById('wysiwygQuoteTitle'); - _wysiwygQuoteUrl = elById('wysiwygQuoteUrl'); + ['author', 'url'].forEach((function (attr) { + elData(this._blockquote, attr, elById(id + '-' + attr).value); + }).bind(this)); - var _keyupCallback = (function(event) { - if (event.which === 13) { - this._dialogSubmit(event); - } - }).bind(this); + this._setTitle(this._blockquote); + this._editor.caret.after(this._blockquote); - _wysiwygQuoteTitle.addEventListener('keyup', _keyupCallback); - _wysiwygQuoteUrl.addEventListener('keyup', _keyupCallback); + this._editor.events.startDetectChanges(); - _wysiwygQuoteButton = elById('wysiwygQuoteSubmit'); - _wysiwygQuoteButton.addEventListener(WCF_CLICK_EVENT, this._dialogSubmit.bind(this)); + UiDialog.close(this); }, - _dialogOnClose: function() { - _element = null; - _insertCallback = null; + /** + * Sets or updates the quote's header title. + * + * @param {Element} blockquote quote element + * @protected + */ + _setTitle: function(blockquote) { + var title = Language.get('wcf.editor.quote.title', { + author: elData(blockquote, 'author'), + url: elData(blockquote, 'url') + }); + + if (elData(blockquote, 'title') !== title) { + elData(blockquote, 'title', title); + } }, _dialogSetup: function() { + var id = 'redactor-quote-' + this._elementId, + idAuthor = id + '-author', + idButtonSave = id + '-button-save', + idUrl = id + '-url'; + return { - id: 'wysiwygQuoteDialog', + id: id, options: { - onClose: this._dialogOnClose.bind(this), - onSetup: this._dialogOnSetup.bind(this) + onSetup: (function() { + elById(idButtonSave).addEventListener(WCF_CLICK_EVENT, this._save.bind(this)); + }).bind(this), + + onShow: (function() { + elById(idAuthor).value = elData(this._blockquote, 'author'); + elById(idUrl).value = elData(this._blockquote, 'url'); + }).bind(this), + + title: Language.get('wcf.editor.quote.edit') }, - source: '
' - + '
' - + '
' + source: '
' + + '
' + + '
' + + '
' + + '' + + '
' + '
' + '
' - + '
' - + '
' + + '
' + + '
' + + '' + + '' + Language.get('wcf.editor.quote.url.description') + '' + + '
' + '
' - + '
' - + '' - + '
' + + '
' + + '
' + + '' + + '
' }; } }; -}); + + return UiRedactorQuote; +}); \ No newline at end of file diff --git a/wcfsetup/install/files/style/bbcode/quote.scss b/wcfsetup/install/files/style/bbcode/quote.scss index 871a69944d..cc537f81cb 100644 --- a/wcfsetup/install/files/style/bbcode/quote.scss +++ b/wcfsetup/install/files/style/bbcode/quote.scss @@ -1,3 +1,4 @@ +.redactor-editor blockquote, .quoteBox { border: 1px solid $wcfContentBorderInner; font-style: italic; @@ -15,6 +16,16 @@ } } +.redactor-editor blockquote::before { + content: attr(data-title); + cursor: pointer; + display: block; + font-style: normal; + margin-bottom: 20px; + + @include wcfFontHeadline; +} + .quoteBoxHeader { align-items: center; display: flex; diff --git a/wcfsetup/install/files/style/ui/redactor.scss b/wcfsetup/install/files/style/ui/redactor.scss index b17a341735..ba88eb5706 100644 --- a/wcfsetup/install/files/style/ui/redactor.scss +++ b/wcfsetup/install/files/style/ui/redactor.scss @@ -109,32 +109,6 @@ } } - .quoteBox { - position: relative; - - &::before { - content: attr(data-quote-header); - cursor: pointer; - display: block; - font-style: normal; - margin-bottom: 20px; - padding-right: 25px; - - @include wcfFontHeadline; - } - - &::after { - content: $fa-var-pencil; - cursor: pointer; - font-family: FontAwesome; - position: absolute; - right: 24px; - top: 10px; - - @include wcfFontHeadline; - } - } - .TODO_codeBox { overflow: hidden; position: relative; diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index abd8fb0e01..24ff34d29b 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -2152,6 +2152,12 @@ Fehler sind beispielsweise: + + + + + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index cda152f34a..d913f3eb42 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -2163,6 +2163,12 @@ Errors are: + + + + + +