4 * @author Alexander Ebert
5 * @copyright 2001-2016 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 * @module WoltLabSuite/Core/Ui/Redactor/Quote
9 define(['Core', 'EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function (Core
, EventHandler
, EventKey
, Language
, StringUtil
, DomUtil
, UiDialog
) {
12 var _headerHeight
= 0;
15 * @param {Object} editor editor instance
16 * @param {jQuery} button toolbar button
19 function UiRedactorQuote(editor
, button
) { this.init(editor
, button
); }
20 UiRedactorQuote
.prototype = {
22 * Initializes the quote management.
24 * @param {Object} editor editor instance
25 * @param {jQuery} button toolbar button
27 init: function(editor
, button
) {
28 this._blockquote
= null;
29 this._editor
= editor
;
30 this._elementId
= this._editor
.$element
[0].id
;
32 EventHandler
.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId
, this._observeLoad
.bind(this));
34 this._editor
.button
.addCallback(button
, this._click
.bind(this));
36 // support for active button marking
37 this._editor
.opts
.activeButtonsStates
.blockquote
= 'woltlabQuote';
39 // static bind to ensure that removing works
40 this._callbackEdit
= this._edit
.bind(this);
42 // bind listeners on init
46 EventHandler
.add('com.woltlab.wcf.redactor2', 'insertQuote_' + this._elementId
, this._insertQuote
.bind(this));
52 * @param {Object} data quote data
55 _insertQuote: function (data
) {
56 this._editor
.buffer
.set();
58 // caret must be within a `<p>`, if it is not move it
60 var block
= this._editor
.selection
.block();
61 if (block
=== false) {
62 this._editor
.selection
.restore();
64 block
= this._editor
.selection
.block();
67 if (block
.nodeName
!== 'P') {
68 var redactor
= this._editor
.core
.editor()[0];
70 // find parent before Redactor
71 while (block
.parentNode
!== redactor
) {
72 block
= block
.parentNode
;
75 // caret.after() requires a following element
76 var next
= this._editor
.caret
.next(block
);
77 if (next
=== undefined || next
.nodeName
!== 'P') {
78 var p
= elCreate('p');
79 p
.textContent
= '\u200B';
81 DomUtil
.insertAfter(p
, block
);
84 this._editor
.caret
.after(block
);
88 if (data
.isText
) content
= this._editor
.marker
.html();
89 else content
= data
.content
;
91 var quoteId
= Core
.getUuid();
92 this._editor
.insert
.html('<blockquote id="' + quoteId
+ '">' + content
+ '</blockquote>');
94 var quote
= elById(quoteId
);
95 elData(quote
, 'author', data
.author
);
96 elData(quote
, 'link', data
.link
);
99 this._editor
.selection
.restore();
100 this._editor
.insert
.text(data
.content
);
103 quote
.removeAttribute('id');
105 this._editor
.caret
.after(quote
);
107 this._editor
.buffer
.set();
111 * Toggles the quote block on button click.
116 this._editor
.button
.toggle({}, 'blockquote', 'func', 'block.format');
118 var blockquote
= this._editor
.selection
.block();
119 if (blockquote
&& blockquote
.nodeName
=== 'BLOCKQUOTE') {
120 this._setTitle(blockquote
);
122 blockquote
.addEventListener(WCF_CLICK_EVENT
, this._callbackEdit
);
127 * Binds event listeners and sets quote title on both editor
128 * initialization and when switching back from code view.
132 _observeLoad: function() {
133 elBySelAll('blockquote', this._editor
.$editor
[0], (function(blockquote
) {
134 blockquote
.addEventListener(WCF_CLICK_EVENT
, this._callbackEdit
);
135 this._setTitle(blockquote
);
140 * Opens the dialog overlay to edit the quote's properties.
142 * @param {Event} event event object
145 _edit: function(event
) {
146 var blockquote
= event
.currentTarget
;
148 if (_headerHeight
=== 0) {
149 _headerHeight
= ~~window
.getComputedStyle(blockquote
).paddingTop
.replace(/px$/, '');
151 var styles
= window
.getComputedStyle(blockquote
, '::before');
152 _headerHeight
+= ~~styles
.paddingTop
.replace(/px$/, '');
153 _headerHeight
+= ~~styles
.height
.replace(/px$/, '');
154 _headerHeight
+= ~~styles
.paddingBottom
.replace(/px$/, '');
157 // check if the click hit the header
158 var offset
= DomUtil
.offset(blockquote
);
159 if (event
.pageY
> offset
.top
&& event
.pageY
< (offset
.top
+ _headerHeight
)) {
160 event
.preventDefault();
162 this._blockquote
= blockquote
;
169 * Saves the changes to the quote's properties.
171 * @param {Event} event event object
174 _save: function(event
) {
175 event
.preventDefault();
177 var id
= 'redactor-quote-' + this._elementId
;
178 var urlInput
= elById(id
+ '-url');
179 var innerError
= elBySel('.innerError', urlInput
.parentNode
);
180 if (innerError
!== null) elRemove(innerError
);
182 var url
= urlInput
.value
.replace(/\u200B/g, '').trim();
183 // simple test to check if it at least looks like it could be a valid url
184 if (url
.length
&& !/^https?:\/\/[^\/]+/.test(url
)) {
185 innerError
= elCreate('small');
186 innerError
.className
= 'innerError';
187 innerError
.textContent
= Language
.get('wcf.editor.quote.url.error.invalid');
188 urlInput
.parentNode
.insertBefore(innerError
, urlInput
.nextElementSibling
);
193 elData(this._blockquote
, 'author', elById(id
+ '-author').value
);
196 elData(this._blockquote
, 'url', url
);
198 this._setTitle(this._blockquote
);
199 this._editor
.caret
.after(this._blockquote
);
201 UiDialog
.close(this);
205 * Sets or updates the quote's header title.
207 * @param {Element} blockquote quote element
210 _setTitle: function(blockquote
) {
211 var title
= Language
.get('wcf.editor.quote.title', {
212 author
: elData(blockquote
, 'author'),
213 url
: elData(blockquote
, 'url')
216 if (elData(blockquote
, 'title') !== title
) {
217 elData(blockquote
, 'title', title
);
221 _dialogSetup: function() {
222 var id
= 'redactor-quote-' + this._elementId
,
223 idAuthor
= id
+ '-author',
224 idButtonSave
= id
+ '-button-save',
230 onSetup
: (function() {
231 elById(idButtonSave
).addEventListener(WCF_CLICK_EVENT
, this._save
.bind(this));
234 onShow
: (function() {
235 elById(idAuthor
).value
= elData(this._blockquote
, 'author');
236 elById(idUrl
).value
= elData(this._blockquote
, 'url');
239 title
: Language
.get('wcf.editor.quote.edit')
241 source
: '<div class="section">'
243 + '<dt><label for="' + idAuthor
+ '">' + Language
.get('wcf.editor.quote.author') + '</label></dt>'
245 + '<input type="text" id="' + idAuthor
+ '" class="long">'
249 + '<dt><label for="' + idUrl
+ '">' + Language
.get('wcf.editor.quote.url') + '</label></dt>'
251 + '<input type="text" id="' + idUrl
+ '" class="long">'
252 + '<small>' + Language
.get('wcf.editor.quote.url.description') + '</small>'
256 + '<div class="formSubmit">'
257 + '<button id="' + idButtonSave
+ '" class="buttonPrimary">' + Language
.get('wcf.global.button.save') + '</button>'
263 return UiRedactorQuote
;