{* WoltLab *}
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabAttachment.js?v={@LAST_UPDATE_TIME}',
+ '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabBlock.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabButton.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabCode.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabColor.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabModal.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}'
+ '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSmiley.js?v={@LAST_UPDATE_TIME}',
+ '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSpoiler.js?v={@LAST_UPDATE_TIME}'
{else}
'{@$__wcf->getPath()}js/3rdParty/redactor2/redactor.min.js?v={@LAST_UPDATE_TIME}',
'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/combined.min.js?v={@LAST_UPDATE_TIME}'
'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}'
+ 'wcf.editor.quote.url.description': '{lang}wcf.editor.quote.url.description{/lang}',
+
+ 'wcf.editor.spoiler.label': '{lang}wcf.editor.spoiler.label{/lang}',
+ 'wcf.editor.spoiler.label.description': '{lang}wcf.editor.spoiler.label.description{/lang}',
+ 'wcf.editor.spoiler.edit': '{lang}wcf.editor.spoiler.edit{/lang}',
+ 'wcf.editor.spoiler.title': '{lang __literal=true}wcf.editor.spoiler.title{/lang}'
});
var buttons = [], buttonOptions = [], customButtons = [];
'source',
'table',
'WoltLabAttachment',
+ 'WoltLabBlock',
'WoltLabCode',
'WoltLabColor',
'WoltLabDropdown',
'WoltLabModal',
'WoltLabQuote',
'WoltLabSize',
- 'WoltLabSmiley'
+ 'WoltLabSmiley',
+ 'WoltLabSpoiler'
],
toolbarFixed: false,
woltlab: {
--- /dev/null
+$.Redactor.prototype.WoltLabBlock = function() {
+ "use strict";
+
+ return {
+ init: function() {
+ this.block.tags = ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'figure'];
+
+ this.block.format = (function(tag, attr, value, type) {
+ tag = (tag === 'quote') ? 'blockquote' : tag;
+
+ // WoltLab modification: move list of allowed elements
+ // outside this method to allow extending it
+ //
+ //this.block.tags = ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'figure'];
+ if ($.inArray(tag, this.block.tags) === -1)
+ {
+ return;
+ }
+
+ if (tag === 'p' && typeof attr === 'undefined')
+ {
+ // remove all
+ attr = 'class';
+ }
+
+ this.placeholder.hide();
+ this.buffer.set();
+
+ return (this.utils.isCollapsed()) ? this.block.formatCollapsed(tag, attr, value, type) : this.block.formatUncollapsed(tag, attr, value, type);
+ }).bind(this);
+ },
+
+ register: function(tag) {
+ if (this.block.tags.indexOf(tag) !== -1) {
+ return;
+ }
+
+ this.block.tags.push(tag);
+
+ if (this.opts.blockTags.indexOf(tag) === -1) {
+ this.opts.blockTags.push(tag);
+
+ this.reIsBlock = new RegExp('^(' + this.opts.blockTags.join('|').toUpperCase() + ')$', 'i');
+ }
+ }
+ }
+};
--- /dev/null
+/**
+ * Manages spoilers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLab/WCF/Ui/Redactor/Spoiler
+ */
+define(['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog) {
+ "use strict";
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @constructor
+ */
+ function UiRedactorSpoiler(editor) { this.init(editor); }
+ UiRedactorSpoiler.prototype = {
+ /**
+ * Initializes the spoiler management.
+ *
+ * @param {Object} editor editor instance
+ */
+ init: function(editor) {
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+ this._spoiler = null;
+
+ 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');
+ this._editor.block.tags.push('woltlab-spoiler');
+
+ // 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);
+
+ // bind listeners on init
+ this._observeLoad();
+ },
+
+ /**
+ * Intercepts the insertion of `[spoiler]` tags and uses
+ * the custom `<woltlab-spoiler>` element instead.
+ *
+ * @param {Object} data event data
+ * @protected
+ */
+ _bbcodeSpoiler: function(data) {
+ data.cancel = true;
+
+ this._editor.button.toggle({}, 'woltlab-spoiler', 'func', 'block.format');
+
+ var spoiler = this._editor.selection.block();
+ if (spoiler && spoiler.nodeName === 'WOLTLAB-SPOILER') {
+ this._setTitle(spoiler);
+
+ spoiler.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ this._editor.events.stopDetectChanges();
+
+ elBySelAll('woltlab-spoiler', this._editor.$editor[0], (function(spoiler) {
+ spoiler.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+ this._setTitle(spoiler);
+ }).bind(this));
+
+ this._editor.events.startDetectChanges();
+ },
+
+ /**
+ * Opens the dialog overlay to edit the spoiler's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var spoiler = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = ~~window.getComputedStyle(spoiler).paddingTop.replace(/px$/, '');
+
+ var styles = window.getComputedStyle(spoiler, '::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(spoiler);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._spoiler = spoiler;
+
+ UiDialog.open(this);
+ }
+ },
+
+ /**
+ * Saves the changes to the spoiler's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _save: function(event) {
+ event.preventDefault();
+
+ this._editor.events.stopDetectChanges();
+
+ elData(this._spoiler, 'label', elById('redactor-spoiler-' + this._elementId + '-label').value);
+
+ this._setTitle(this._spoiler);
+ this._editor.caret.after(this._spoiler);
+
+ this._editor.events.startDetectChanges();
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * Sets or updates the spoiler's header title.
+ *
+ * @param {Element} spoiler spoiler element
+ * @protected
+ */
+ _setTitle: function(spoiler) {
+ var title = Language.get('wcf.editor.spoiler.title', { label: elData(spoiler, 'label') });
+
+ if (elData(spoiler, 'title') !== title) {
+ elData(spoiler, 'title', title);
+ }
+ },
+
+ _dialogSetup: function() {
+ var id = 'redactor-spoiler-' + this._elementId,
+ idButtonSave = id + '-button-save',
+ idLabel = id + '-label';
+
+ return {
+ id: id,
+ options: {
+ onSetup: (function() {
+ elById(idButtonSave).addEventListener(WCF_CLICK_EVENT, this._save.bind(this));
+ }).bind(this),
+
+ onShow: (function() {
+ elById(idLabel).value = elData(this._spoiler, 'label');
+ }).bind(this),
+
+ title: Language.get('wcf.editor.spoiler.edit')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="' + idLabel + '">' + Language.get('wcf.editor.spoiler.label') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idLabel + '" class="long">'
+ + '<small>' + Language.get('wcf.editor.spoiler.label.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button id="' + idButtonSave + '" class="buttonPrimary">' + Language.get('wcf.global.button.save') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+
+ return UiRedactorSpoiler;
+});
\ No newline at end of file