html = html.replace(/<p><br([^>]+)?><\/p>/g, '<p>@@@wcf_empty_line@@@</p>');
return $mpSyncClean.call(self, html);
};
+
+ if (this.getOption('wAutosaveOnce')) {
+ this._saveTextToStorage();
+ delete this.opts.wAutosaveOnce;
+ }
},
/**
var $parts = html.split(/(<\/?p>)/);
var $tmp = '';
var $buffer = '';
- for (var $i = 1; $i < $parts.length; $i++) {
+ for (var $i = 0; $i < $parts.length; $i++) {
var $part = $parts[$i];
if ($part == '<p>') {
continue;
$buffer = '';
}
else {
- $buffer += $part;
+ if ($i == 0) {
+ $tmp += $part;
+ }
+ else {
+ $buffer += $part;
+ }
}
}
html = $tmp;
html = html.replace(/<\/(ul|ol)>/gi, '[/list]');
// [table]
- html = html.replace(/<table[^>]*>/gi, '[table]');
- html = html.replace(/<\/table>/gi, '[/table]');
+ html = html.replace(/<table[^>]*>/gi, '[table]\n');
+ html = html.replace(/<\/table>/gi, '[/table]\n');
// remove <tbody>
html = html.replace(/<tbody>([\s\S]*?)<\/tbody>/, function(match, p1) {
// remove empty <tr>s
html = html.replace(/<tr><\/tr>/gi, '');
// [tr]
- html = html.replace(/<tr>/gi, '[tr]');
- html = html.replace(/<\/tr>/gi, '[/tr]');
+ html = html.replace(/<tr>/gi, '[tr]\n');
+ html = html.replace(/<\/tr>/gi, '[/tr]\n');
// [td]+[align]
html = html.replace(/<td style="text-align: ?(left|center|right|justify);? ?">([\s\S]*?)<\/td>/gi, "[td][align=$1]$2[/align][/td]");
// [td]
- html = html.replace(/<td>/gi, '[td]');
- html = html.replace(/<\/td>/gi, '[/td]');
+ html = html.replace(/(\t)*<td>/gi, '[td]');
+ html = html.replace(/(\t)*<\/td>/gi, '[/td]\n');
// cache redactor's selection markers
var $cachedMarkers = { };
this.$modal.children('.dialogContent').removeClass('dialogForm');
},
+ tableInsert: function()
+ {
+ this.bufferSet(false);
+
+ var rows = $('#redactor_table_rows').val(),
+ columns = $('#redactor_table_columns').val(),
+ $table_box = $('<div></div>'),
+ tableId = Math.floor(Math.random() * 99999),
+ $table = $('<table id="table' + tableId + '"><tbody></tbody></table>'),
+ i, $row, z, $column;
+
+ for (i = 0; i < rows; i++)
+ {
+ $row = $('<tr></tr>');
+
+ for (z = 0; z < columns; z++)
+ {
+ $column = $('<td>' + this.opts.invisibleSpace + '</td>');
+
+ // set the focus to the first td
+ if (i === 0 && z === 0)
+ {
+ $column.append('<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>');
+ }
+
+ $($row).append($column);
+ }
+
+ $table.append($row);
+ }
+
+ $table_box.append($table);
+ var html = $table_box.html();
+
+ if (this.opts.linebreaks === false && this.browser('mozilla'))
+ {
+ html += '<p>' + this.opts.invisibleSpace + '</p>';
+ }
+
+ this.modalClose();
+ this.selectionRestore();
+
+ var current = this.getBlock() || this.getCurrent();
+
+ if (current && current.tagName != 'BODY')
+ {
+ // WoltLab fix for nested tables
+ if (current.tagName == 'TD') {
+ $(current).append(html);
+ }
+ else {
+ if (current.tagName == 'LI')
+ {
+ var current = $(current).closest('ul, ol');
+ }
+
+ $(current).after(html);
+ }
+ // WoltLab fix for nested tables
+ }
+ else
+ {
+ this.insertHtmlAdvanced(html, false);
+ }
+
+ this.selectionRestore();
+
+ var table = this.$editor.find('#table' + tableId);
+ this.buttonActiveObserver();
+
+ table.find('span#selection-marker-1, inline#selection-marker-1').remove();
+ table.removeAttr('id');
+
+ this.sync();
+ },
+
modalOpenedCallback: function() {
// handle positioning of form submit controls
var $heightDifference = 0;
--- /dev/null
+if (!RedactorPlugins) var RedactorPlugins = {};
+
+/**
+ * Provides message option tabs for Redactor.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+RedactorPlugins.woptions = {
+ /**
+ * list of message option elements
+ * @var object<object>
+ */
+ _messageOptions: { },
+
+ /**
+ * message option container
+ * @var jQuery
+ */
+ _messageOptionContainer: null,
+
+ /**
+ * navigation container
+ * @var jQuery
+ */
+ _messageOptionNavigation: null,
+
+ /**
+ * Initializes the RedactorPlugins.woptions plugin.
+ */
+ init: function() {
+ var $options = this.getOption('wMessageOptions');
+ if (!$options.length) {
+ return;
+ }
+
+ this._messageOptionContainer = $('<div id="redactorMessageOptions" class="redactorMessageOptions" />').appendTo(this.$box);
+ this._messageOptionNavigation = $('<nav><ul /></nav>').appendTo(this._messageOptionContainer).children('ul');
+
+ for (var $i = 0; $i < $options.length; $i++) {
+ var $container = $options[$i];
+
+ var $listItem = $('<li><a>' + $container.title + '</a></li>').appendTo(this._messageOptionNavigation);
+ $listItem.data('containerID', $container.containerID).click($.proxy(this._showMessageOptionContainer, this));
+
+ var $tabContainer = $('<div class="redactorMessageOptionContainer" id="redactorMessageOptions_' + $container.containerID + '" />').hide().appendTo(this._messageOptionContainer);
+
+ for (var $j = 0; $j < $container.items.length; $j++) {
+ $($container.items[$j]).appendTo($tabContainer);
+ }
+
+ this._messageOptions[$container.containerID] = {
+ container: $tabContainer,
+ listItem: $listItem
+ };
+ }
+
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'updateMessageOptions', this._messageOptions);
+
+ WCF.System.Event.addListener('com.woltlab.wcf.redactor', 'reset', $.proxy(this._wOptionsListener, this));
+ },
+
+ /**
+ * Toggles the specified message option container.
+ *
+ * @param object event
+ * @param string containerID
+ */
+ _showMessageOptionContainer: function(event, containerID) {
+ var $containerID = (event === null) ? containerID : $(event.currentTarget).data('containerID');
+ if (this._messageOptions[$containerID].listItem.hasClass('active')) {
+ this._messageOptions[$containerID].listItem.removeClass('active');
+ this._messageOptions[$containerID].container.hide();
+
+ return;
+ }
+
+ $.each(this._messageOptions, function(containerID, elements) {
+ if (containerID == $containerID) {
+ elements.listItem.addClass('active');
+ elements.container.show();
+ }
+ else {
+ elements.listItem.removeClass('active');
+ elements.container.hide();
+ }
+ });
+ },
+
+ /**
+ * Collapses all message option containers.
+ *
+ * @param object data
+ */
+ _wOptionsListener: function(data) {
+ $.each(this._messageOptions, function(containerID, elements) {
+ elements.listItem.removeClass('active');
+ elements.container.hide();
+
+ elements.container.find('input, select, textarea').each(function(index, element) {
+ var $element = $(element);
+ switch ($element.getTagName()) {
+ case 'input':
+ $element.prop('checked', false);
+ break;
+
+ default:
+ $element.val('');
+ break;
+ }
+ });
+ });
+
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'updateMessageOptions', this._messageOptions);
+ }
+};
this.buttonAwesome('upload', 'fa-upload');
this._initAttachments();
+
+ WCF.System.Event.addListener('com.woltlab.wcf.redactor', 'reset', $.proxy(this._wUploadListener, this));
},
/**
* Initializes the attachments user interface.
*/
_initAttachments: function() {
- this._attachmentsContainer = $('<div class="redactorAttachmentContainer" />').hide().appendTo(this.$box);
+ this._attachmentsContainer = $('#redactorMessageOptions_attachments');
var $attachmentList = $('<ul class="formAttachmentList clearfix" />').hide().appendTo(this._attachmentsContainer);
$('<dl class="wide"><dt></dt><dd><div data-max-size="{@$attachmentHandler->getMaxSize()}"></div><small>' + WCF.String.unescapeHTML(WCF.Language.get('wcf.attachment.upload.limits')) + '</small></dd></dl>').appendTo(this._attachmentsContainer);
$listItem.appendTo($attachmentList);
- this._attachmentsContainer.show();
$attachmentList.show();
}
+
+ this._showMessageOptionContainer(null, 'attachments');
}
new WCF.Attachment.Upload(this._attachmentsContainer.find('> dl > dd > div'), this._attachmentsContainer.children('ul'), $options.objectType, $options.objectID, $options.tmpHash, $options.parentObjectID, $options.maxCount, this.$source.wcfIdentify());
new WCF.Action.Delete('wcf\\data\\attachment\\AttachmentAction', '.formAttachmentList > li');
+ },
+
+ _wUploadListener: function(data) {
+ this._attachmentsContainer.children('.formAttachmentList').hide().empty();
+ this._attachmentsContainer.find('.jsButtonAttachmentInsertAll').hide();
}
};
this.autosaveEnable();
if (this.getOption('wautosave').saveOnInit || this.$source.data('saveOnInit')) {
- this._saveTextToStorage();
+ this.setOption('wAutosaveOnce', true);
}
else {
this.autosaveRestore();
else {
this.$source.val('');
}
+
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'reset');
},
/**
this._makeSortable();
- this._insertAllButton = $('<p class="button">' + WCF.Language.get('wcf.attachment.insertAll') + '</p>').hide().appendTo(this._buttonSelector);
+ this._insertAllButton = $('<p class="button jsButtonAttachmentInsertAll">' + WCF.Language.get('wcf.attachment.insertAll') + '</p>').hide().appendTo(this._buttonSelector);
this._insertAllButton.click($.proxy(this._insertAll, this));
if (this._fileListSelector.children('li:not(.uploadFailed)').length) {
$parameters.anchor = this._container.data('anchor');
}
+ // check for additional settings
+ var $container = $('#redactorMessageOptions_settings');
+ if ($container.length) {
+ $parameters.settings = { };
+ $container.find('input, textarea, select').each(function(index, element) {
+ var $element = $(element);
+ switch ($element.getTagName()) {
+ case 'input':
+ switch ($element.prop('type')) {
+ case 'checkbox':
+ case 'radio':
+ if ($element.is(':checked')) {
+ $parameters.settings[$element.prop('name')] = $element.val();
+ }
+ else if ($element.data('submitEmpty')) {
+ $parameters.settings[$element.prop('name')] = 0;
+ }
+ break;
+
+ default:
+ $parameters.settings[$element.prop('name')] = $element.val();
+ break;
+ }
+ break;
+
+ default:
+ $parameters.settings[$element.prop('name')] = $element.val();
+ break;
+ }
+ });
+ }
+
return $parameters;
},
var $objectID = $button.data('objectID');
this._buttons[$objectID] = $button.click($.proxy(this._click, this));
}, this));
+
+ WCF.System.Event.addListener('com.woltlab.wcf.objectWatch', 'update', $.proxy(this._updateSubscriptionStatus, this));
},
/**
else if (data.actionName === 'saveSubscription' && this._dialog.is(':visible')) {
this._dialog.wcfDialog('close');
- // update icon
- var $icon = $(this._buttonSelector + '[data-object-id=' + data.returnValues.objectID + '] > .icon');
- if (data.returnValues.subscribe) {
- $icon.removeClass('icon-bookmark-empty').addClass('icon-bookmark');
- }
- else {
- $icon.removeClass('icon-bookmark').addClass('icon-bookmark-empty');
- }
+ this._updateSubscriptionStatus({
+ isSubscribed: data.returnValues.subscribe,
+ objectID: data.returnValues.objectID
+ });
+
// show notification
if (this._notification === null) {
}
});
this._proxy.sendRequest();
+ },
+
+ /**
+ * Updates subscription status and icon.
+ *
+ * @param object data
+ */
+ _updateSubscriptionStatus: function(data) {
+ var $button = $(this._buttonSelector + '[data-object-id=' + data.objectID + ']');
+ var $icon = $button.children('.icon');
+ if (data.isSubscribed) {
+ $icon.removeClass('icon-bookmark-empty').addClass('icon-bookmark');
+ $button.data('isSubscribed', true);
+ }
+ else {
+ $icon.removeClass('icon-bookmark').addClass('icon-bookmark-empty');
+ $button.data('isSubscribed', false);
+ }
+
+ WCF.System.Event.fireEvent('com.woltlab.wcf.objectWatch', 'updatedSubscription', data);
}
});
}
}
}
-}
+};
+
+/**
+ * System-wide event system.
+ */
+WCF.System.Event = {
+ /**
+ * list of event listeners grouped by identifier and action.
+ * @var object<object>
+ */
+ _listeners: { },
+
+ /**
+ * Registers a new event listener.
+ *
+ * @param string identifier
+ * @param string action
+ * @param object listener
+ */
+ addListener: function(identifier, action, listener) {
+ if (typeof this._listeners[identifier] === 'undefined') {
+ this._listeners[identifier] = { };
+ }
+
+ if (typeof this._listeners[identifier][action] === 'undefined') {
+ this._listeners[identifier][action] = [ ];
+ }
+
+ this._listeners[identifier][action].push(listener);
+ },
+
+ /**
+ * Fires a new event and notifies all registered event listeners.
+ *
+ * @param string identifier
+ * @param string action
+ * @param object data
+ */
+ fireEvent: function(identifier, action, data) {
+ data = data || { };
+
+ if (this._listeners[identifier] && this._listeners[identifier][action]) {
+ for (var $i = 0; $i < this._listeners[identifier][action].length; $i++) {
+ this._listeners[identifier][action][$i](data);
+ }
+ }
+ }
+};
/**
* Worker support for frontend based upon DatabaseObjectActions.