}
});
-/**
- * Provides previews for Redactor message fields.
- *
- * @param string className
- * @param string messageFieldID
- * @param string previewButtonID
- */
-WCF.Message.Preview = Class.extend({
- /**
- * class name
- * @var string
- */
- _className: '',
-
- /**
- * message field id
- * @var string
- */
- _messageFieldID: '',
-
- /**
- * message field
- * @var jQuery
- */
- _messageField: null,
-
- /**
- * action proxy
- * @var WCF.Action.Proxy
- */
- _proxy: null,
-
- /**
- * preview button
- * @var jQuery
- */
- _previewButton: null,
-
- /**
- * previous button label
- * @var string
- */
- _previewButtonLabel: '',
-
- /**
- * Initializes a new WCF.Message.Preview object.
- *
- * @param string className
- * @param string messageFieldID
- * @param string previewButtonID
- */
- init: function(className, messageFieldID, previewButtonID) {
- this._className = className;
-
- // validate message field
- this._messageFieldID = $.wcfEscapeID(messageFieldID);
- this._textarea = $('#' + this._messageFieldID);
- if (!this._textarea.length) {
- console.debug("[WCF.Message.Preview] Unable to find message field identified by '" + this._messageFieldID + "'");
- return;
- }
-
- // validate preview button
- previewButtonID = $.wcfEscapeID(previewButtonID);
- this._previewButton = $('#' + previewButtonID);
- if (!this._previewButton.length) {
- console.debug("[WCF.Message.Preview] Unable to find preview button identified by '" + previewButtonID + "'");
- return;
+if (COMPILER_TARGET_DEFAULT) {
+ /**
+ * Provides previews for Redactor message fields.
+ *
+ * @param string className
+ * @param string messageFieldID
+ * @param string previewButtonID
+ */
+ WCF.Message.Preview = Class.extend({
+ /**
+ * class name
+ * @var string
+ */
+ _className: '',
+
+ /**
+ * message field id
+ * @var string
+ */
+ _messageFieldID: '',
+
+ /**
+ * message field
+ * @var jQuery
+ */
+ _messageField: null,
+
+ /**
+ * action proxy
+ * @var WCF.Action.Proxy
+ */
+ _proxy: null,
+
+ /**
+ * preview button
+ * @var jQuery
+ */
+ _previewButton: null,
+
+ /**
+ * previous button label
+ * @var string
+ */
+ _previewButtonLabel: '',
+
+ /**
+ * Initializes a new WCF.Message.Preview object.
+ *
+ * @param string className
+ * @param string messageFieldID
+ * @param string previewButtonID
+ */
+ init: function (className, messageFieldID, previewButtonID) {
+ this._className = className;
+
+ // validate message field
+ this._messageFieldID = $.wcfEscapeID(messageFieldID);
+ this._textarea = $('#' + this._messageFieldID);
+ if (!this._textarea.length) {
+ console.debug("[WCF.Message.Preview] Unable to find message field identified by '" + this._messageFieldID + "'");
+ return;
+ }
+
+ // validate preview button
+ previewButtonID = $.wcfEscapeID(previewButtonID);
+ this._previewButton = $('#' + previewButtonID);
+ if (!this._previewButton.length) {
+ console.debug("[WCF.Message.Preview] Unable to find preview button identified by '" + previewButtonID + "'");
+ return;
+ }
+
+ this._previewButton.click($.proxy(this._click, this));
+ this._proxy = new WCF.Action.Proxy({
+ failure: $.proxy(this._failure, this),
+ success: $.proxy(this._success, this)
+ });
+ },
+
+ /**
+ * Reads message field input and triggers an AJAX request.
+ */
+ _click: function (event) {
+ var $message = this._getMessage();
+ if ($message === null) {
+ console.debug("[WCF.Message.Preview] Unable to access Redactor instance of '" + this._messageFieldID + "'");
+ return;
+ }
+
+ this._proxy.setOption('data', {
+ actionName: 'getMessagePreview',
+ className: this._className,
+ parameters: this._getParameters($message)
+ });
+ this._proxy.sendRequest();
+
+ // update button label
+ this._previewButtonLabel = this._previewButton.html();
+ this._previewButton.html(WCF.Language.get('wcf.global.loading')).disable();
+
+ // poke event
+ event.stopPropagation();
+ return false;
+ },
+
+ /**
+ * Returns request parameters.
+ *
+ * @param string message
+ * @return object
+ */
+ _getParameters: function (message) {
+ // collect message form options
+ var $options = {};
+ $('#settings_' + this._messageFieldID).find('input[type=checkbox]').each(function (index, checkbox) {
+ var $checkbox = $(checkbox);
+ if ($checkbox.is(':checked')) {
+ $options[$checkbox.prop('name')] = $checkbox.prop('value');
+ }
+ });
+
+ // build parameters
+ return {
+ data: {
+ message: message
+ },
+ options: $options
+ };
+ },
+
+ /**
+ * Returns parsed message from Redactor or null if editor was not accessible.
+ *
+ * @return string
+ */
+ _getMessage: function () {
+ return this._textarea.redactor('code.get');
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param object data
+ * @param string textStatus
+ * @param jQuery jqXHR
+ */
+ _success: function (data, textStatus, jqXHR) {
+ // restore preview button
+ this._previewButton.html(this._previewButtonLabel).enable();
+
+ // remove error message
+ this._textarea.parent().children('small.innerError').remove();
+
+ // evaluate message
+ this._handleResponse(data);
+ },
+
+ /**
+ * Evaluates response data.
+ *
+ * @param object data
+ */
+ _handleResponse: function (data) {
+ },
+
+ /**
+ * Handles errors during preview requests.
+ *
+ * The return values indicates if the default error overlay is shown.
+ *
+ * @param object data
+ * @return boolean
+ */
+ _failure: function (data) {
+ if (data === null || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+ return true;
+ }
+
+ // restore preview button
+ this._previewButton.html(this._previewButtonLabel).enable();
+
+ var $innerError = this._textarea.parent().children('small.innerError').empty();
+ if (!$innerError.length) {
+ $innerError = $('<small class="innerError" />').appendTo(this._textarea.parent());
+ }
+
+ var message = (data.returnValues.errorType === 'empty' ? WCF.Language.get('wcf.global.form.error.empty') : data.returnValues.errorMessage);
+ if (data.returnValues.realErrorMessage) message = data.returnValues.realErrorMessage;
+ $innerError.html(message);
+
+ return false;
}
-
- this._previewButton.click($.proxy(this._click, this));
- this._proxy = new WCF.Action.Proxy({
- failure: $.proxy(this._failure, this),
- success: $.proxy(this._success, this)
- });
- },
+ });
/**
- * Reads message field input and triggers an AJAX request.
+ * Default implementation for message previews.
+ *
+ * @see WCF.Message.Preview
*/
- _click: function(event) {
- var $message = this._getMessage();
- if ($message === null) {
- console.debug("[WCF.Message.Preview] Unable to access Redactor instance of '" + this._messageFieldID + "'");
- return;
- }
+ WCF.Message.DefaultPreview = WCF.Message.Preview.extend({
+ _dialog: null,
+ _options: {},
- this._proxy.setOption('data', {
- actionName: 'getMessagePreview',
- className: this._className,
- parameters: this._getParameters($message)
- });
- this._proxy.sendRequest();
-
- // update button label
- this._previewButtonLabel = this._previewButton.html();
- this._previewButton.html(WCF.Language.get('wcf.global.loading')).disable();
-
- // poke event
- event.stopPropagation();
- return false;
- },
-
- /**
- * Returns request parameters.
- *
- * @param string message
- * @return object
- */
- _getParameters: function(message) {
- // collect message form options
- var $options = { };
- $('#settings_' + this._messageFieldID).find('input[type=checkbox]').each(function(index, checkbox) {
- var $checkbox = $(checkbox);
- if ($checkbox.is(':checked')) {
- $options[$checkbox.prop('name')] = $checkbox.prop('value');
+ /**
+ * @see WCF.Message.Preview.init()
+ */
+ init: function (options) {
+ if (arguments.length > 1 && typeof options === 'string') {
+ throw new Error("Outdated API call, please update your implementation.");
+ }
+
+ this._options = $.extend({
+ disallowedBBCodesPermission: 'user.message.disallowedBBCodes',
+ messageFieldID: '',
+ previewButtonID: '',
+ messageObjectType: '',
+ messageObjectID: 0
+ }, options);
+
+ if (!this._options.messageObjectType) {
+ throw new Error("Field 'messageObjectType' cannot be empty.");
+ }
+
+ this._super('wcf\\data\\bbcode\\MessagePreviewAction', this._options.messageFieldID, this._options.previewButtonID);
+ },
+
+ /**
+ * @see WCF.Message.Preview._handleResponse()
+ */
+ _handleResponse: function (data) {
+ require(['WoltLabSuite/Core/Ui/Dialog'], (function (UiDialog) {
+ UiDialog.open(this, '<div class="htmlContent">' + data.returnValues.message + '</div>');
+ }).bind(this));
+ },
+
+ /**
+ * @see WCF.Message.Preview._getParameters()
+ */
+ _getParameters: function (message) {
+ var $parameters = this._super(message);
+
+ for (var key in this._options) {
+ if (this._options.hasOwnProperty(key) && key !== 'messageFieldID' && key !== 'previewButtonID') {
+ $parameters[key] = this._options[key];
+ }
+ }
+
+ return $parameters;
+ },
+
+ _dialogSetup: function () {
+ return {
+ id: 'messagePreview',
+ options: {
+ title: WCF.Language.get('wcf.global.preview')
+ },
+ source: null
}
- });
-
- // build parameters
- return {
- data: {
- message: message
- },
- options: $options
- };
- },
-
- /**
- * Returns parsed message from Redactor or null if editor was not accessible.
- *
- * @return string
- */
- _getMessage: function() {
- return this._textarea.redactor('code.get');
- },
-
- /**
- * Handles successful AJAX requests.
- *
- * @param object data
- * @param string textStatus
- * @param jQuery jqXHR
- */
- _success: function(data, textStatus, jqXHR) {
- // restore preview button
- this._previewButton.html(this._previewButtonLabel).enable();
-
- // remove error message
- this._textarea.parent().children('small.innerError').remove();
-
- // evaluate message
- this._handleResponse(data);
- },
-
- /**
- * Evaluates response data.
- *
- * @param object data
- */
- _handleResponse: function(data) { },
-
- /**
- * Handles errors during preview requests.
- *
- * The return values indicates if the default error overlay is shown.
- *
- * @param object data
- * @return boolean
- */
- _failure: function(data) {
- if (data === null || data.returnValues === undefined || data.returnValues.errorType === undefined) {
- return true;
- }
-
- // restore preview button
- this._previewButton.html(this._previewButtonLabel).enable();
-
- var $innerError = this._textarea.parent().children('small.innerError').empty();
- if (!$innerError.length) {
- $innerError = $('<small class="innerError" />').appendTo(this._textarea.parent());
- }
-
- $innerError.html((data.returnValues.errorType === 'empty' ? WCF.Language.get('wcf.global.form.error.empty') : data.returnValues.errorMessage));
-
- return false;
- }
-});
-
-/**
- * Default implementation for message previews.
- *
- * @see WCF.Message.Preview
- */
-WCF.Message.DefaultPreview = WCF.Message.Preview.extend({
- _dialog: null,
- _options: {},
-
- /**
- * @see WCF.Message.Preview.init()
- */
- init: function(options) {
- if (arguments.length > 1 && typeof options === 'string') {
- throw new Error("Outdated API call, please update your implementation.");
}
-
- this._options = $.extend({
- disallowedBBCodesPermission: 'user.message.disallowedBBCodes',
- messageFieldID: '',
- previewButtonID: '',
- messageObjectType: '',
- messageObjectID: 0
- }, options);
-
- if (!this._options.messageObjectType) {
- throw new Error("Field 'messageObjectType' cannot be empty.");
+ });
+
+ /**
+ * Handles multilingualism for messages.
+ *
+ * @param integer languageID
+ * @param object availableLanguages
+ * @param boolean forceSelection
+ */
+ WCF.Message.Multilingualism = Class.extend({
+ /**
+ * list of available languages
+ * @var object
+ */
+ _availableLanguages: {},
+
+ /**
+ * language id
+ * @var integer
+ */
+ _languageID: 0,
+
+ /**
+ * language input element
+ * @var jQuery
+ */
+ _languageInput: null,
+
+ /**
+ * Initializes WCF.Message.Multilingualism
+ *
+ * @param integer languageID
+ * @param object availableLanguages
+ * @param boolean forceSelection
+ */
+ init: function (languageID, availableLanguages, forceSelection) {
+ this._availableLanguages = availableLanguages;
+ this._languageID = languageID || 0;
+
+ this._languageInput = $('#languageID');
+
+ // preselect current language id
+ this._updateLabel();
+
+ // register event listener
+ this._languageInput.find('.dropdownMenu > li').click($.proxy(this._click, this));
+
+ // add element to disable multilingualism
+ if (!forceSelection) {
+ var $dropdownMenu = this._languageInput.find('.dropdownMenu');
+ $('<li class="dropdownDivider" />').appendTo($dropdownMenu);
+ $('<li><span><span class="badge">' + this._availableLanguages[0] + '</span></span></li>').click($.proxy(this._disable, this)).appendTo($dropdownMenu);
+ }
+
+ // bind submit event
+ this._languageInput.parents('form').submit($.proxy(this._submit, this));
+ },
+
+ /**
+ * Handles language selections.
+ *
+ * @param object event
+ */
+ _click: function (event) {
+ this._languageID = $(event.currentTarget).data('languageID');
+ this._updateLabel();
+ },
+
+ /**
+ * Disables language selection.
+ */
+ _disable: function () {
+ this._languageID = 0;
+ this._updateLabel();
+ },
+
+ /**
+ * Updates selected language.
+ */
+ _updateLabel: function () {
+ this._languageInput.find('.dropdownToggle > span').text(this._availableLanguages[this._languageID]);
+ },
+
+ /**
+ * Sets language id upon submit.
+ */
+ _submit: function () {
+ this._languageInput.next('input[name=languageID]').prop('value', this._languageID);
+ }
+ });
+
+ /**
+ * Loads smiley categories upon user request.
+ */
+ WCF.Message.SmileyCategories = Class.extend({
+ /**
+ * list of already loaded category ids
+ * @var array<integer>
+ */
+ _cache: [],
+
+ /**
+ * action proxy
+ * @var WCF.Action.Proxy
+ */
+ _proxy: null,
+
+ /**
+ * wysiwyg editor selector
+ * @var string
+ */
+ _wysiwygSelector: '',
+
+ /**
+ * Initializes the smiley loader.
+ *
+ * @param string wysiwygSelector
+ */
+ init: function (wysiwygSelector) {
+ this._proxy = new WCF.Action.Proxy({
+ success: $.proxy(this._success, this)
+ });
+ this._wysiwygSelector = wysiwygSelector;
+
+ $('#smilies-' + this._wysiwygSelector).on('messagetabmenushow', $.proxy(this._click, this));
+ },
+
+ /**
+ * Handles tab menu clicks.
+ *
+ * @param {Event} event
+ * @param {Object} data
+ */
+ _click: function (event, data) {
+ event.preventDefault();
+
+ var $categoryID = parseInt(data.activeTab.tab.data('smileyCategoryID'));
+
+ // ignore global category, will always be pre-loaded
+ if (!$categoryID) {
+ return;
+ }
+
+ // smilies have already been loaded for this tab, ignore
+ if (data.activeTab.container.children('ul.smileyList').length) {
+ return;
+ }
+
+ // cache exists
+ if (this._cache[$categoryID] !== undefined) {
+ data.activeTab.container.html(this._cache[$categoryID]);
+ return;
+ }
+
+ // load content
+ this._proxy.setOption('data', {
+ actionName: 'getSmilies',
+ className: 'wcf\\data\\smiley\\category\\SmileyCategoryAction',
+ objectIDs: [$categoryID]
+ });
+ this._proxy.sendRequest();
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param object data
+ * @param string textStatus
+ * @param jQuery jqXHR
+ */
+ _success: function (data, textStatus, jqXHR) {
+ var $categoryID = parseInt(data.returnValues.smileyCategoryID);
+ this._cache[$categoryID] = data.returnValues.template;
+
+ $('#smilies-' + this._wysiwygSelector + '-' + $categoryID).html(data.returnValues.template);
}
-
- this._super('wcf\\data\\bbcode\\MessagePreviewAction', this._options.messageFieldID, this._options.previewButtonID);
- },
-
- /**
- * @see WCF.Message.Preview._handleResponse()
- */
- _handleResponse: function(data) {
- require(['WoltLabSuite/Core/Ui/Dialog'], (function(UiDialog) {
- UiDialog.open(this, '<div class="htmlContent">' + data.returnValues.message + '</div>');
- }).bind(this));
- },
+ });
/**
- * @see WCF.Message.Preview._getParameters()
+ * Handles smiley clicks.
+ *
+ * @param string wysiwygSelector
*/
- _getParameters: function(message) {
- var $parameters = this._super(message);
+ WCF.Message.Smilies = Class.extend({
+ /**
+ * wysiwyg editor id
+ * @var string
+ */
+ _editorId: '',
- for (var key in this._options) {
- if (this._options.hasOwnProperty(key) && key !== 'messageFieldID' && key !== 'previewButtonID') {
- $parameters[key] = this._options[key];
+ /**
+ * Initializes the smiley handler.
+ *
+ * @param {string} editorId
+ */
+ init: function (editorId) {
+ this._editorId = editorId;
+
+ $('.messageTabMenu[data-wysiwyg-container-id=' + this._editorId + ']').on('mousedown', '.jsSmiley', this._smileyClick.bind(this));
+ },
+
+ /**
+ * Handles tab smiley clicks.
+ *
+ * @param {Event} event
+ */
+ _smileyClick: function (event) {
+ event.preventDefault();
+
+ require(['EventHandler'], (function (EventHandler) {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'insertSmiley_' + this._editorId, {
+ img: event.currentTarget.children[0]
+ });
+ }).bind(this));
+ }
+ });
+
+ /**
+ * Provides an inline message editor.
+ *
+ * @deprecated 3.0 - please use `WoltLabSuite/Core/Ui/Message/InlineEditor` instead
+ *
+ * @param integer containerID
+ */
+ WCF.Message.InlineEditor = Class.extend({
+ /**
+ * list of messages
+ * @var object
+ */
+ _container: {},
+
+ /**
+ * container id
+ * @var int
+ */
+ _containerID: 0,
+
+ /**
+ * list of dropdowns
+ * @var object
+ */
+ _dropdowns: {},
+
+ /**
+ * CSS selector for the message container
+ * @var string
+ */
+ _messageContainerSelector: '.jsMessage',
+
+ /**
+ * prefix of the message editor CSS id
+ * @var string
+ */
+ _messageEditorIDPrefix: 'messageEditor',
+
+ /**
+ * Initializes a new WCF.Message.InlineEditor object.
+ *
+ * @param integer containerID
+ * @param boolean supportExtendedForm
+ * @param WCF.Message.Quote.Manager quoteManager
+ */
+ init: function (containerID, supportExtendedForm, quoteManager) {
+ require(['WoltLabSuite/Core/Ui/Message/InlineEditor'], (function (UiMessageInlineEditor) {
+ new UiMessageInlineEditor({
+ className: this._getClassName(),
+ containerId: containerID,
+ editorPrefix: this._messageEditorIDPrefix,
+
+ messageSelector: this._messageContainerSelector,
+ quoteManager: quoteManager || null,
+
+ callbackDropdownInit: this._callbackDropdownInit.bind(this)
+ });
+ }).bind(this));
+ },
+
+ /**
+ * Loads WYSIWYG editor for selected message.
+ *
+ * @param object event
+ * @param integer containerID
+ * @return boolean
+ */
+ _click: function (event, containerID) {
+ containerID = (event === null) ? ~~containerID : ~~elData(event.currentTarget, 'container-id');
+
+ require(['WoltLabSuite/Core/Ui/Message/InlineEditor'], (function (UiMessageInlineEditor) {
+ UiMessageInlineEditor.legacyEdit(containerID);
+ }).bind(this));
+
+ if (event) {
+ event.preventDefault();
}
- }
-
- return $parameters;
- },
-
- _dialogSetup: function() {
- return {
- id: 'messagePreview',
- options: {
- title: WCF.Language.get('wcf.global.preview')
- },
- source: null
- }
- }
-});
+ },
+
+ /**
+ * Initializes the inline edit dropdown menu.
+ *
+ * @param integer containerID
+ * @param jQuery dropdownMenu
+ */
+ _initDropdownMenu: function (containerID, dropdownMenu) {
+ },
+
+ _callbackDropdownInit: function (element, dropdownMenu) {
+ this._initDropdownMenu($(element).wcfIdentify(), $(dropdownMenu));
+
+ return null;
+ },
+
+ /**
+ * Returns message action class name.
+ *
+ * @return string
+ */
+ _getClassName: function () {
+ return '';
+ }
+ });
+
+ /**
+ * Handles submit buttons for forms with an embedded WYSIWYG editor.
+ */
+ WCF.Message.Submit = {
+ /**
+ * list of registered buttons
+ * @var object
+ */
+ _buttons: {},
+
+ /**
+ * Registers submit button for specified wysiwyg container id.
+ *
+ * @param string wysiwygContainerID
+ * @param string selector
+ */
+ registerButton: function (wysiwygContainerID, selector) {
+ if (!WCF.Browser.isChrome()) {
+ return;
+ }
+
+ this._buttons[wysiwygContainerID] = $(selector);
+ },
+
+ /**
+ * Triggers 'click' event for registered buttons.
+ */
+ execute: function (wysiwygContainerID) {
+ if (!this._buttons[wysiwygContainerID]) {
+ return;
+ }
+
+ this._buttons[wysiwygContainerID].trigger('click');
+ }
+ };
+}
+else {
+ WCF.Message.Preview = Class.extend({
+ _className: "",
+ _messageFieldID: "",
+ _messageField: {},
+ _proxy: {},
+ _previewButton: {},
+ _previewButtonLabel: "",
+ init: function() {},
+ _click: function() {},
+ _getParameters: function() {},
+ _getMessage: function() {},
+ _success: function() {},
+ _handleResponse: function() {},
+ _failure: function() {}
+ });
+
+ WCF.Message.DefaultPreview = WCF.Message.Preview.extend({
+ _dialog: {},
+ _options: {},
+ init: function() {},
+ _handleResponse: function() {},
+ _getParameters: function() {},
+ _dialogSetup: function() {},
+ _className: "",
+ _messageFieldID: "",
+ _messageField: {},
+ _proxy: {},
+ _previewButton: {},
+ _previewButtonLabel: "",
+ _click: function() {},
+ _getMessage: function() {},
+ _success: function() {},
+ _failure: function() {}
+ });
+
+ WCF.Message.Multilingualism = Class.extend({
+ _availableLanguages: {},
+ _languageID: 0,
+ _languageInput: {},
+ init: function() {},
+ _click: function() {},
+ _disable: function() {},
+ _updateLabel: function() {},
+ _submit: function() {}
+ });
+
+ WCF.Message.SmileyCategories = Class.extend({
+ _cache: {},
+ _proxy: {},
+ _wysiwygSelector: "",
+ init: function() {},
+ _click: function() {},
+ _success: function() {}
+ });
+
+ WCF.Message.Smilies = Class.extend({
+ _editorId: "",
+ init: function() {},
+ _smileyClick: function() {}
+ });
+
+ WCF.Message.InlineEditor = Class.extend({
+ _container: {},
+ _containerID: 0,
+ _dropdowns: {},
+ _messageContainerSelector: "",
+ _messageEditorIDPrefix: "",
+ init: function() {},
+ _click: function() {},
+ _initDropdownMenu: function() {},
+ _callbackDropdownInit: function() {},
+ _getClassName: function() {}
+ });
+
+ WCF.Message.Submit = {
+ _buttons: {},
+ registerButton: function() {},
+ execute: function() {}
+ };
+}
/**
- * Handles multilingualism for messages.
- *
- * @param integer languageID
- * @param object availableLanguages
- * @param boolean forceSelection
+ * Namespace for message quotes.
*/
-WCF.Message.Multilingualism = Class.extend({
- /**
- * list of available languages
- * @var object
- */
- _availableLanguages: { },
-
- /**
- * language id
- * @var integer
- */
- _languageID: 0,
-
- /**
- * language input element
- * @var jQuery
- */
- _languageInput: null,
-
- /**
- * Initializes WCF.Message.Multilingualism
- *
- * @param integer languageID
- * @param object availableLanguages
- * @param boolean forceSelection
- */
- init: function(languageID, availableLanguages, forceSelection) {
- this._availableLanguages = availableLanguages;
- this._languageID = languageID || 0;
-
- this._languageInput = $('#languageID');
-
- // preselect current language id
- this._updateLabel();
-
- // register event listener
- this._languageInput.find('.dropdownMenu > li').click($.proxy(this._click, this));
-
- // add element to disable multilingualism
- if (!forceSelection) {
- var $dropdownMenu = this._languageInput.find('.dropdownMenu');
- $('<li class="dropdownDivider" />').appendTo($dropdownMenu);
- $('<li><span><span class="badge">' + this._availableLanguages[0] + '</span></span></li>').click($.proxy(this._disable, this)).appendTo($dropdownMenu);
- }
-
- // bind submit event
- this._languageInput.parents('form').submit($.proxy(this._submit, this));
- },
-
- /**
- * Handles language selections.
- *
- * @param object event
- */
- _click: function(event) {
- this._languageID = $(event.currentTarget).data('languageID');
- this._updateLabel();
- },
-
- /**
- * Disables language selection.
- */
- _disable: function() {
- this._languageID = 0;
- this._updateLabel();
- },
-
- /**
- * Updates selected language.
- */
- _updateLabel: function() {
- this._languageInput.find('.dropdownToggle > span').text(this._availableLanguages[this._languageID]);
- },
-
- /**
- * Sets language id upon submit.
- */
- _submit: function() {
- this._languageInput.next('input[name=languageID]').prop('value', this._languageID);
- }
-});
-
-/**
- * Loads smiley categories upon user request.
- */
-WCF.Message.SmileyCategories = Class.extend({
- /**
- * list of already loaded category ids
- * @var array<integer>
- */
- _cache: [ ],
-
- /**
- * action proxy
- * @var WCF.Action.Proxy
- */
- _proxy: null,
-
- /**
- * wysiwyg editor selector
- * @var string
- */
- _wysiwygSelector: '',
-
- /**
- * Initializes the smiley loader.
- *
- * @param string wysiwygSelector
- */
- init: function(wysiwygSelector) {
- this._proxy = new WCF.Action.Proxy({
- success: $.proxy(this._success, this)
- });
- this._wysiwygSelector = wysiwygSelector;
-
- $('#smilies-' + this._wysiwygSelector).on('messagetabmenushow', $.proxy(this._click, this));
- },
-
- /**
- * Handles tab menu clicks.
- *
- * @param {Event} event
- * @param {Object} data
- */
- _click: function(event, data) {
- event.preventDefault();
-
- var $categoryID = parseInt(data.activeTab.tab.data('smileyCategoryID'));
-
- // ignore global category, will always be pre-loaded
- if (!$categoryID) {
- return;
- }
-
- // smilies have already been loaded for this tab, ignore
- if (data.activeTab.container.children('ul.smileyList').length) {
- return;
- }
-
- // cache exists
- if (this._cache[$categoryID] !== undefined) {
- data.activeTab.container.html(this._cache[$categoryID]);
- return;
- }
-
- // load content
- this._proxy.setOption('data', {
- actionName: 'getSmilies',
- className: 'wcf\\data\\smiley\\category\\SmileyCategoryAction',
- objectIDs: [ $categoryID ]
- });
- this._proxy.sendRequest();
- },
-
- /**
- * Handles successful AJAX requests.
- *
- * @param object data
- * @param string textStatus
- * @param jQuery jqXHR
- */
- _success: function(data, textStatus, jqXHR) {
- var $categoryID = parseInt(data.returnValues.smileyCategoryID);
- this._cache[$categoryID] = data.returnValues.template;
-
- $('#smilies-' + this._wysiwygSelector + '-' + $categoryID).html(data.returnValues.template);
- }
-});
+WCF.Message.Quote = { };
-/**
- * Handles smiley clicks.
- *
- * @param string wysiwygSelector
- */
-WCF.Message.Smilies = Class.extend({
- /**
- * wysiwyg editor id
- * @var string
- */
- _editorId: '',
-
- /**
- * Initializes the smiley handler.
- *
- * @param {string} editorId
- */
- init: function(editorId) {
- this._editorId = editorId;
-
- $('.messageTabMenu[data-wysiwyg-container-id=' + this._editorId + ']').on('mousedown', '.jsSmiley', this._smileyClick.bind(this));
- },
-
- /**
- * Handles tab smiley clicks.
- *
- * @param {Event} event
- */
- _smileyClick: function(event) {
- event.preventDefault();
-
- require(['EventHandler'], (function(EventHandler) {
- EventHandler.fire('com.woltlab.wcf.redactor2', 'insertSmiley_' + this._editorId, {
- img: event.currentTarget.children[0]
+if (COMPILER_TARGET_DEFAULT) {
+ /**
+ * Handles message quotes.
+ */
+ WCF.Message.Quote.Handler = Class.extend({
+ /**
+ * active container id
+ * @var string
+ */
+ _activeContainerID: '',
+
+ /**
+ * action class name
+ * @var string
+ */
+ _className: '',
+
+ /**
+ * list of message containers
+ * @var object
+ */
+ _containers: {},
+
+ /**
+ * container selector
+ * @var string
+ */
+ _containerSelector: '',
+
+ /**
+ * 'copy quote' overlay
+ * @var jQuery
+ */
+ _copyQuote: null,
+
+ /**
+ * marked message
+ * @var string
+ */
+ _message: '',
+
+ /**
+ * message body selector
+ * @var string
+ */
+ _messageBodySelector: '',
+
+ /**
+ * object id
+ * @var {int}
+ */
+ _objectID: 0,
+
+ /**
+ * object type name
+ * @var string
+ */
+ _objectType: '',
+
+ /**
+ * action proxy
+ * @var WCF.Action.Proxy
+ */
+ _proxy: null,
+
+ /**
+ * quote manager
+ * @var WCF.Message.Quote.Manager
+ */
+ _quoteManager: null,
+
+ /**
+ * Initializes the quote handler for given object type.
+ *
+ * @param {WCF.Message.Quote.Manager} quoteManager
+ * @param {string} className
+ * @param {string} objectType
+ * @param {string} containerSelector
+ * @param {string} messageBodySelector
+ * @param {string} messageContentSelector
+ * @param {boolean} supportDirectInsert
+ */
+ init: function (quoteManager, className, objectType, containerSelector, messageBodySelector, messageContentSelector, supportDirectInsert) {
+ this._className = className;
+ if (this._className === '') {
+ console.debug("[WCF.Message.QuoteManager] Empty class name given, aborting.");
+ return;
+ }
+
+ this._objectType = objectType;
+ if (this._objectType === '') {
+ console.debug("[WCF.Message.QuoteManager] Empty object type name given, aborting.");
+ return;
+ }
+
+ this._containerSelector = containerSelector;
+ this._message = '';
+ this._messageBodySelector = messageBodySelector;
+ this._objectID = 0;
+ this._proxy = new WCF.Action.Proxy({
+ success: $.proxy(this._success, this)
});
- }).bind(this));
- }
-});
-
-/**
- * Provides an inline message editor.
- *
- * @deprecated 3.0 - please use `WoltLabSuite/Core/Ui/Message/InlineEditor` instead
- *
- * @param integer containerID
- */
-WCF.Message.InlineEditor = Class.extend({
- /**
- * list of messages
- * @var object
- */
- _container: { },
-
- /**
- * container id
- * @var int
- */
- _containerID: 0,
-
- /**
- * list of dropdowns
- * @var object
- */
- _dropdowns: { },
-
- /**
- * CSS selector for the message container
- * @var string
- */
- _messageContainerSelector: '.jsMessage',
-
- /**
- * prefix of the message editor CSS id
- * @var string
- */
- _messageEditorIDPrefix: 'messageEditor',
-
- /**
- * Initializes a new WCF.Message.InlineEditor object.
- *
- * @param integer containerID
- * @param boolean supportExtendedForm
- * @param WCF.Message.Quote.Manager quoteManager
- */
- init: function(containerID, supportExtendedForm, quoteManager) {
- require(['WoltLabSuite/Core/Ui/Message/InlineEditor'], (function(UiMessageInlineEditor) {
- new UiMessageInlineEditor({
- className: this._getClassName(),
- containerId: containerID,
- editorPrefix: this._messageEditorIDPrefix,
-
- messageSelector: this._messageContainerSelector,
+
+ this._initContainers();
+
+ supportDirectInsert = (supportDirectInsert && quoteManager.supportPaste());
+ this._initCopyQuote(supportDirectInsert);
+
+ $(document).mouseup($.proxy(this._mouseUp, this));
+
+ // register with quote manager
+ this._quoteManager = quoteManager;
+ this._quoteManager.register(this._objectType, this);
+
+ // register with DOMNodeInsertedHandler
+ WCF.DOMNodeInsertedHandler.addCallback('WCF.Message.Quote.Handler' + objectType.hashCode(), $.proxy(this._initContainers, this));
+ },
+
+ /**
+ * Initializes message containers.
+ */
+ _initContainers: function () {
+ var self = this;
+ $(this._containerSelector).each(function (index, container) {
+ var $container = $(container);
+ var $containerID = $container.wcfIdentify();
- callbackDropdownInit: this._callbackDropdownInit.bind(this)
- });
- }).bind(this));
- },
-
- /**
- * Loads WYSIWYG editor for selected message.
- *
- * @param object event
- * @param integer containerID
- * @return boolean
- */
- _click: function(event, containerID) {
- containerID = (event === null) ? ~~containerID : ~~elData(event.currentTarget, 'container-id');
-
- require(['WoltLabSuite/Core/Ui/Message/InlineEditor'], (function(UiMessageInlineEditor) {
- UiMessageInlineEditor.legacyEdit(containerID);
- }).bind(this));
-
- if (event) {
- event.preventDefault();
- }
- },
-
- /**
- * Initializes the inline edit dropdown menu.
- *
- * @param integer containerID
- * @param jQuery dropdownMenu
- */
- _initDropdownMenu: function(containerID, dropdownMenu) { },
-
- _callbackDropdownInit: function(element, dropdownMenu) {
- this._initDropdownMenu($(element).wcfIdentify(), $(dropdownMenu));
-
- return null;
- },
-
- /**
- * Returns message action class name.
- *
- * @return string
- */
- _getClassName: function() {
- return '';
- }
-});
-
-/**
- * Handles submit buttons for forms with an embedded WYSIWYG editor.
- */
-WCF.Message.Submit = {
- /**
- * list of registered buttons
- * @var object
- */
- _buttons: { },
-
- /**
- * Registers submit button for specified wysiwyg container id.
- *
- * @param string wysiwygContainerID
- * @param string selector
- */
- registerButton: function(wysiwygContainerID, selector) {
- if (!WCF.Browser.isChrome()) {
- return;
- }
-
- this._buttons[wysiwygContainerID] = $(selector);
- },
-
- /**
- * Triggers 'click' event for registered buttons.
- */
- execute: function(wysiwygContainerID) {
- if (!this._buttons[wysiwygContainerID]) {
- return;
- }
-
- this._buttons[wysiwygContainerID].trigger('click');
- }
-};
-
-/**
- * Namespace for message quotes.
- */
-WCF.Message.Quote = { };
-
-/**
- * Handles message quotes.
- */
-WCF.Message.Quote.Handler = Class.extend({
- /**
- * active container id
- * @var string
- */
- _activeContainerID: '',
-
- /**
- * action class name
- * @var string
- */
- _className: '',
-
- /**
- * list of message containers
- * @var object
- */
- _containers: { },
-
- /**
- * container selector
- * @var string
- */
- _containerSelector: '',
-
- /**
- * 'copy quote' overlay
- * @var jQuery
- */
- _copyQuote: null,
-
- /**
- * marked message
- * @var string
- */
- _message: '',
-
- /**
- * message body selector
- * @var string
- */
- _messageBodySelector: '',
-
- /**
- * object id
- * @var integer
- */
- _objectID: 0,
-
- /**
- * object type name
- * @var string
- */
- _objectType: '',
-
- /**
- * action proxy
- * @var WCF.Action.Proxy
- */
- _proxy: null,
-
- /**
- * quote manager
- * @var WCF.Message.Quote.Manager
- */
- _quoteManager: null,
-
- /**
- * Initializes the quote handler for given object type.
- *
- * @param {WCF.Message.Quote.Manager} quoteManager
- * @param {string} className
- * @param {string} objectType
- * @param {string} containerSelector
- * @param {string} messageBodySelector
- * @param {string} messageContentSelector
- * @param {boolean} supportDirectInsert
- */
- init: function(quoteManager, className, objectType, containerSelector, messageBodySelector, messageContentSelector, supportDirectInsert) {
- this._className = className;
- if (this._className == '') {
- console.debug("[WCF.Message.QuoteManager] Empty class name given, aborting.");
- return;
- }
-
- this._objectType = objectType;
- if (this._objectType == '') {
- console.debug("[WCF.Message.QuoteManager] Empty object type name given, aborting.");
- return;
- }
-
- this._containerSelector = containerSelector;
- this._message = '';
- this._messageBodySelector = messageBodySelector;
- this._messageContentSelector = messageContentSelector;
- this._objectID = 0;
- this._proxy = new WCF.Action.Proxy({
- success: $.proxy(this._success, this)
- });
-
- this._initContainers();
-
- supportDirectInsert = (supportDirectInsert && quoteManager.supportPaste()) ? true : false;
- this._initCopyQuote(supportDirectInsert);
-
- $(document).mouseup($.proxy(this._mouseUp, this));
-
- // register with quote manager
- this._quoteManager = quoteManager;
- this._quoteManager.register(this._objectType, this);
-
- // register with DOMNodeInsertedHandler
- WCF.DOMNodeInsertedHandler.addCallback('WCF.Message.Quote.Handler' + objectType.hashCode(), $.proxy(this._initContainers, this));
- },
-
- /**
- * Initializes message containers.
- */
- _initContainers: function() {
- var self = this;
- $(this._containerSelector).each(function(index, container) {
- var $container = $(container);
- var $containerID = $container.wcfIdentify();
-
- if (!self._containers[$containerID]) {
- self._containers[$containerID] = $container;
- if ($container.hasClass('jsInvalidQuoteTarget')) {
- return true;
+ if (!self._containers[$containerID]) {
+ self._containers[$containerID] = $container;
+ if ($container.hasClass('jsInvalidQuoteTarget')) {
+ return true;
+ }
+
+ if (self._messageBodySelector) {
+ $container.data('body', $container.find(self._messageBodySelector).data('containerID', $containerID));
+ }
+
+ $container.mousedown($.proxy(self._mouseDown, self));
+
+ // bind event to quote whole message
+ self._containers[$containerID].find('.jsQuoteMessage').click($.proxy(self._saveFullQuote, self));
}
-
- if (self._messageBodySelector) {
- $container.data('body', $container.find(self._messageBodySelector).data('containerID', $containerID));
+ });
+ },
+
+ /**
+ * Handles mouse down event.
+ *
+ * @param {Event} event
+ */
+ _mouseDown: function (event) {
+ // hide copy quote
+ this._copyQuote.removeClass('active');
+
+ this._activeContainerID = (event.currentTarget.classList.contains('jsInvalidQuoteTarget')) ? '' : event.currentTarget.id;
+ },
+
+ /**
+ * Returns the text of a node and its children.
+ *
+ * @param {Node} node
+ * @return {string}
+ */
+ _getNodeText: function (node) {
+ // work-around for IE, see http://stackoverflow.com/a/5983176
+ var $nodeFilter = function (node) {
+ switch (node.tagName) {
+ case 'BLOCKQUOTE':
+ case 'SCRIPT':
+ return NodeFilter.FILTER_REJECT;
+
+ case 'IMG':
+ if (!node.classList.contains('smiley') || node.alt.length === 0) {
+ return NodeFilter.FILTER_REJECT;
+ }
+ // fallthrough
+
+ //noinspection FallthroughInSwitchStatementJS
+ default:
+ return NodeFilter.FILTER_ACCEPT;
}
+ };
+ $nodeFilter.acceptNode = $nodeFilter;
+
+ var $walker = document.createTreeWalker(
+ node,
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
+ $nodeFilter,
+ true
+ );
+
+ var $text = '', ignoreLinks = [], value;
+ while ($walker.nextNode()) {
+ var $node = $walker.currentNode;
- $container.mousedown($.proxy(self._mouseDown, self));
-
- // bind event to quote whole message
- self._containers[$containerID].find('.jsQuoteMessage').click($.proxy(self._saveFullQuote, self));
- }
- });
- },
-
- /**
- * Handles mouse down event.
- *
- * @param {Event} event
- */
- _mouseDown: function(event) {
- // hide copy quote
- this._copyQuote.removeClass('active');
-
- this._activeContainerID = (event.currentTarget.classList.contains('jsInvalidQuoteTarget')) ? '' : event.currentTarget.id;
- },
-
- /**
- * Returns the text of a node and its children.
- *
- * @param {Node} node
- * @return {string}
- */
- _getNodeText: function(node) {
- // work-around for IE, see http://stackoverflow.com/a/5983176
- var $nodeFilter = function(node) {
- switch (node.tagName) {
- case 'BLOCKQUOTE':
- case 'IMG':
- case 'SCRIPT':
- return NodeFilter.FILTER_REJECT;
- break;
-
- default:
- return NodeFilter.FILTER_ACCEPT;
- break;
- }
- };
- $nodeFilter.acceptNode = $nodeFilter;
-
- var $walker = document.createTreeWalker(
- node,
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
- $nodeFilter,
- true
- );
-
- var $text = '';
- while ($walker.nextNode()) {
- var $node = $walker.currentNode;
-
- if ($node.nodeType === Node.ELEMENT_NODE) {
- switch ($node.tagName) {
- case 'BR':
- case 'LI':
- case 'UL':
- $text += "\n";
- break;
-
- case 'TD':
- if (!$.browser.msie) {
+ if ($node.nodeType === Node.ELEMENT_NODE) {
+ switch ($node.tagName) {
+ case 'A':
+ // \u2026 === …
+ value = $node.textContent;
+ if (value.indexOf('\u2026') > 0) {
+ var tmp = value.split(/\u2026/);
+ if (tmp.length === 2) {
+ var href = $node.href;
+ if (href.indexOf(tmp[0]) === 0 && href.substr(tmp[1].length * -1) === tmp[1]) {
+ // truncated url, use original href to preserve link
+ $text += href;
+ ignoreLinks.push($node);
+ }
+ }
+ }
+ break;
+
+ case 'BR':
+ case 'LI':
+ case 'UL':
$text += "\n";
- }
- break;
+ break;
+
+ case 'TD':
+ if (!$.browser.msie) {
+ $text += "\n";
+ }
+ break;
+
+ case 'P':
+ $text += "\n\n";
+ break;
+
+ // smilies
+ case 'IMG':
+ $text += " " + $node.alt + " ";
+ break;
+ }
+ }
+ else {
+ if ($node.parentNode.nodeName === 'A' && ignoreLinks.indexOf($node.parentNode) !== -1) {
+ // ignore text content of links that have already been captured
+ continue;
+ }
- case 'P':
- $text += "\n\n";
- break;
+ $text += $node.nodeValue.replace(/\n/g, '');
}
+
}
- else {
- $text += $node.nodeValue.replace(/\n/g, '');
+
+ return $text;
+ },
+
+ /**
+ * Handles the mouse up event.
+ */
+ _mouseUp: function () {
+ // ignore event
+ if (this._activeContainerID === '') {
+ this._copyQuote.removeClass('active');
+ return;
}
- }
-
- return $text;
- },
-
- /**
- * Handles the mouse up event.
- */
- _mouseUp: function() {
- // ignore event
- if (this._activeContainerID === '') {
- this._copyQuote.removeClass('active');
- return;
- }
-
- var selection = window.getSelection();
- if (selection.rangeCount !== 1 || selection.isCollapsed) {
- this._copyQuote.removeClass('active');
- return;
- }
-
- var $container = this._containers[this._activeContainerID];
- var $objectID = $container.data('objectID');
- $container = $container.data('body') || $container;
-
- var anchorNode = selection.anchorNode;
- while (anchorNode) {
- if (anchorNode === $container[0]) {
- break;
+ var selection = window.getSelection();
+ if (selection.rangeCount !== 1 || selection.isCollapsed) {
+ this._copyQuote.removeClass('active');
+ return;
}
- anchorNode = anchorNode.parentNode;
- }
-
- // selection spans unrelated nodes
- if (anchorNode !== $container[0]) {
- this._copyQuote.removeClass('active');
- return;
- }
-
- var $selection = this._getSelectedText();
- var $text = $.trim($selection);
- if ($text == '') {
- this._copyQuote.removeClass('active');
+ var $container = this._containers[this._activeContainerID];
+ var $objectID = $container.data('objectID');
+ $container = $container.data('body') || $container;
- return;
- }
-
- // check if mousedown/mouseup took place inside a blockquote
- var range = selection.getRangeAt(0);
- var startContainer = (range.startContainer.nodeType === Node.TEXT_NODE) ? range.startContainer.parentNode : range.startContainer;
- var endContainer = (range.endContainer.nodeType === Node.TEXT_NODE) ? range.endContainer.parentNode : range.endContainer;
- if (startContainer.closest('blockquote') || endContainer.closest('blockquote')) {
- this._copyQuote.removeClass('active');
+ var anchorNode = selection.anchorNode;
+ while (anchorNode) {
+ if (anchorNode === $container[0]) {
+ break;
+ }
+
+ anchorNode = anchorNode.parentNode;
+ }
- return;
- }
-
- // compare selection with message text of given container
- var $messageText = this._getNodeText($container[0]);
-
- // selected text is not part of $messageText or contains text from unrelated nodes
- if (this._normalize($messageText).indexOf(this._normalize($text)) === -1) {
- return;
- }
- this._copyQuote.addClass('active');
-
- var $coordinates = this._getBoundingRectangle($container, window.getSelection());
- var $dimensions = this._copyQuote.getDimensions('outer');
- var $left = ($coordinates.right - $coordinates.left) / 2 - ($dimensions.width / 2) + $coordinates.left;
-
- this._copyQuote.css({
- top: $coordinates.top - $dimensions.height - 7 + 'px',
- left: $left + 'px'
- });
- this._copyQuote.removeClass('active');
-
- // reset containerID
- this._activeContainerID = '';
-
- // show element after a delay, to prevent display if text was unmarked again (clicking into marked text)
- var self = this;
- window.setTimeout(function() {
- var $text = $.trim(self._getSelectedText());
- if ($text != '') {
- self._copyQuote.addClass('active');
- self._message = $text;
- self._objectID = $objectID;
+ // selection spans unrelated nodes
+ if (anchorNode !== $container[0]) {
+ this._copyQuote.removeClass('active');
+ return;
}
- }, 10);
- },
-
- /**
- * Normalizes a text for comparison.
- *
- * @param {string} text
- * @return {string}
- */
- _normalize: function(text) {
- return text.replace(/\r?\n|\r/g, "\n").replace(/\s/g, ' ').replace(/\s{2,}/g, ' ');
- },
-
- /**
- * Returns the offsets of the selection's bounding rectangle.
- *
- * @return {Object}
- */
- _getBoundingRectangle: function(container, selection) {
- var $coordinates = null;
-
- if (selection.rangeCount > 0) {
- // the coordinates returned by getBoundingClientRect() are relative to the viewport, not the document!
- var $rect = selection.getRangeAt(0).getBoundingClientRect();
- $coordinates = {
- left: $rect.left,
- right: $rect.right,
- top: $rect.top + $(document).scrollTop()
- };
- }
-
- return $coordinates;
- },
-
- /**
- * Initializes the 'copy quote' element.
- *
- * @param {boolean} supportDirectInsert
- */
- _initCopyQuote: function(supportDirectInsert) {
- this._copyQuote = $('#quoteManagerCopy');
- if (!this._copyQuote.length) {
- this._copyQuote = $('<div id="quoteManagerCopy" class="balloonTooltip interactive"><span class="jsQuoteManagerStore">' + WCF.Language.get('wcf.message.quote.quoteSelected') + '</span></div>').appendTo(document.body);
- var $storeQuote = this._copyQuote.children('span.jsQuoteManagerStore').click($.proxy(this._saveQuote, this));
- if (supportDirectInsert) {
- $('<span class="jsQuoteManagerQuoteAndInsert">' + WCF.Language.get('wcf.message.quote.quoteAndReply') + '</span>').click($.proxy(this._saveAndInsertQuote, this)).insertAfter($storeQuote);
+ var $selection = this._getSelectedText();
+ var $text = $.trim($selection);
+ if ($text == '') {
+ this._copyQuote.removeClass('active');
+
+ return;
}
- }
- },
-
- /**
- * Returns the text selection.
- *
- * @return string
- */
- _getSelectedText: function() {
- var $selection = window.getSelection();
- if ($selection.rangeCount) {
- return this._getNodeText($selection.getRangeAt(0).cloneContents());
- }
-
- return '';
- },
-
- /**
- * Saves a full quote.
- *
- * @param {Event} event
- */
- _saveFullQuote: function(event) {
- event.preventDefault();
-
- var $listItem = $(event.currentTarget);
-
- this._proxy.setOption('data', {
- actionName: 'saveFullQuote',
- className: this._className,
- interfaceName: 'wcf\\data\\IMessageQuoteAction',
- objectIDs: [ $listItem.data('objectID') ]
- });
- this._proxy.sendRequest();
-
- // mark element as quoted
- if ($listItem.data('isQuoted')) {
- $listItem.data('isQuoted', false).children('a').removeClass('active');
- }
- else {
- $listItem.data('isQuoted', true).children('a').addClass('active');
- }
-
- // close navigation on mobile
- var $navigationList = $listItem.parents('.buttonGroupNavigation');
- if ($navigationList.hasClass('jsMobileButtonGroupNavigation')) {
- $navigationList.children('.dropdownLabel').trigger('click');
- }
- },
-
- /**
- * Saves a quote.
- *
- * @param {boolean} renderQuote
- */
- _saveQuote: function(renderQuote) {
- this._proxy.setOption('data', {
- actionName: 'saveQuote',
- className: this._className,
- interfaceName: 'wcf\\data\\IMessageQuoteAction',
- objectIDs: [ this._objectID ],
- parameters: {
- message: this._message,
- renderQuote: (renderQuote === true)
+
+ // check if mousedown/mouseup took place inside a blockquote
+ var range = selection.getRangeAt(0);
+ var startContainer = (range.startContainer.nodeType === Node.TEXT_NODE) ? range.startContainer.parentNode : range.startContainer;
+ var endContainer = (range.endContainer.nodeType === Node.TEXT_NODE) ? range.endContainer.parentNode : range.endContainer;
+ if (startContainer.closest('blockquote') || endContainer.closest('blockquote')) {
+ this._copyQuote.removeClass('active');
+
+ return;
}
- });
- this._proxy.sendRequest();
- },
-
- /**
- * Saves a quote and directly inserts it.
- */
- _saveAndInsertQuote: function() {
- this._saveQuote(true);
- },
-
- /**
- * Handles successful AJAX requests.
- *
- * @param {Object} data
- */
- _success: function(data) {
- if (data.returnValues.count !== undefined) {
- if (data.returnValues.fullQuoteMessageIDs !== undefined) {
- data.returnValues.fullQuoteObjectIDs = data.returnValues.fullQuoteMessageIDs;
+
+ // compare selection with message text of given container
+ var $messageText = this._getNodeText($container[0]);
+
+ // selected text is not part of $messageText or contains text from unrelated nodes
+ if (this._normalize($messageText).indexOf(this._normalize($text)) === -1) {
+ return;
}
+ this._copyQuote.addClass('active');
- var $fullQuoteObjectIDs = (data.returnValues.fullQuoteObjectIDs !== undefined) ? data.returnValues.fullQuoteObjectIDs : { };
- this._quoteManager.updateCount(data.returnValues.count, $fullQuoteObjectIDs);
- }
-
- switch (data.actionName) {
- case 'saveQuote':
- case 'saveFullQuote':
- if (data.returnValues.renderedQuote) {
- WCF.System.Event.fireEvent('com.woltlab.wcf.message.quote', 'insert', {
- forceInsert: (data.actionName === 'saveQuote'),
- quote: data.returnValues.renderedQuote
- });
+ var $coordinates = this._getBoundingRectangle($container, window.getSelection());
+ var $dimensions = this._copyQuote.getDimensions('outer');
+ var $left = ($coordinates.right - $coordinates.left) / 2 - ($dimensions.width / 2) + $coordinates.left;
+
+ this._copyQuote.css({
+ top: $coordinates.top - $dimensions.height - 7 + 'px',
+ left: $left + 'px'
+ });
+ this._copyQuote.removeClass('active');
+
+ // reset containerID
+ this._activeContainerID = '';
+
+ // show element after a delay, to prevent display if text was unmarked again (clicking into marked text)
+ var self = this;
+ window.setTimeout(function () {
+ var $text = $.trim(self._getSelectedText());
+ if ($text != '') {
+ self._copyQuote.addClass('active');
+ self._message = $text;
+ self._objectID = $objectID;
}
- break;
- }
- },
-
- /**
- * Updates the full quote data for all matching objects.
- *
- * @param array<integer> $objectIDs
- */
- updateFullQuoteObjectIDs: function(objectIDs) {
- for (var $containerID in this._containers) {
- this._containers[$containerID].find('.jsQuoteMessage').each(function(index, button) {
- // reset all markings
- var $button = $(button).data('isQuoted', 0);
- $button.children('a').removeClass('active');
+ }, 10);
+ },
+
+ /**
+ * Normalizes a text for comparison.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ _normalize: function (text) {
+ return text.replace(/\r?\n|\r/g, "\n").replace(/\s/g, ' ').replace(/\s{2,}/g, ' ');
+ },
+
+ /**
+ * Returns the offsets of the selection's bounding rectangle.
+ *
+ * @return {Object}
+ */
+ _getBoundingRectangle: function (container, selection) {
+ var $coordinates = null;
+
+ if (selection.rangeCount > 0) {
+ // the coordinates returned by getBoundingClientRect() are relative to the viewport, not the document!
+ var $rect = selection.getRangeAt(0).getBoundingClientRect();
- // mark as active
- if (WCF.inArray($button.data('objectID'), objectIDs)) {
- $button.data('isQuoted', 1).children('a').addClass('active');
+ $coordinates = {
+ left: $rect.left,
+ right: $rect.right,
+ top: $rect.top + $(document).scrollTop()
+ };
+ }
+
+ return $coordinates;
+ },
+
+ /**
+ * Initializes the 'copy quote' element.
+ *
+ * @param {boolean} supportDirectInsert
+ */
+ _initCopyQuote: function (supportDirectInsert) {
+ this._copyQuote = $('#quoteManagerCopy');
+ if (!this._copyQuote.length) {
+ this._copyQuote = $('<div id="quoteManagerCopy" class="balloonTooltip interactive"><span class="jsQuoteManagerStore">' + WCF.Language.get('wcf.message.quote.quoteSelected') + '</span></div>').appendTo(document.body);
+ var $storeQuote = this._copyQuote.children('span.jsQuoteManagerStore').click($.proxy(this._saveQuote, this));
+ if (supportDirectInsert) {
+ $('<span class="jsQuoteManagerQuoteAndInsert">' + WCF.Language.get('wcf.message.quote.quoteAndReply') + '</span>').click($.proxy(this._saveAndInsertQuote, this)).insertAfter($storeQuote);
}
+ }
+ },
+
+ /**
+ * Returns the text selection.
+ *
+ * @return string
+ */
+ _getSelectedText: function () {
+ var $selection = window.getSelection();
+ if ($selection.rangeCount) {
+ return this._getNodeText($selection.getRangeAt(0).cloneContents());
+ }
+
+ return '';
+ },
+
+ /**
+ * Saves a full quote.
+ *
+ * @param {Event} event
+ */
+ _saveFullQuote: function (event) {
+ event.preventDefault();
+
+ var $listItem = $(event.currentTarget);
+
+ this._proxy.setOption('data', {
+ actionName: 'saveFullQuote',
+ className: this._className,
+ interfaceName: 'wcf\\data\\IMessageQuoteAction',
+ objectIDs: [$listItem.data('objectID')]
});
- }
- }
-});
-
-/**
- * Manages stored quotes.
- *
- * @param integer count
- */
-WCF.Message.Quote.Manager = Class.extend({
- /**
- * list of form buttons
- * @var {Object}
- */
- _buttons: {},
-
- /**
- * number of stored quotes
- * @var {int}
- */
- _count: 0,
-
- /**
- * dialog overlay
- * @var {jQuery}
- */
- _dialog: null,
-
- /**
- * editor element id
- * @var {string}
- */
- _editorId: '',
-
- /**
- * alternative editor element id
- * @var {string}
- */
- _editorIdAlternative: '',
-
- /**
- * form element
- * @var {jQuery}
- */
- _form: null,
-
- /**
- * list of quote handlers
- * @var {Object}
- */
- _handlers: {},
-
- /**
- * true, if an up-to-date template exists
- * @var {boolean}
- */
- _hasTemplate: false,
-
- /**
- * true, if related quotes should be inserted
- * @var {boolean}
- */
- _insertQuotes: true,
-
- /**
- * action proxy
- * @var {WCF.Action.Proxy}
- */
- _proxy: null,
-
- /**
- * list of quotes to remove upon submit
- * @var {Array}
- */
- _removeOnSubmit: [ ],
-
- /**
- * allow pasting
- * @var {boolean}
- */
- _supportPaste: false,
-
- /**
- * pasting was temporarily enabled due to an alternative editor being set
- * @var boolean
- */
- _supportPasteOverride: false,
-
- /**
- * Initializes the quote manager.
- *
- * @param {int} count
- * @param {string} elementID
- * @param {boolean} supportPaste
- * @param {Array} removeOnSubmit
- */
- init: function(count, elementID, supportPaste, removeOnSubmit) {
- this._buttons = {
- insert: null,
- remove: null
- };
- this._count = parseInt(count) || 0;
- this._dialog = null;
- this._editorId = '';
- this._editorIdAlternative = '';
- this._form = null;
- this._handlers = { };
- this._hasTemplate = false;
- this._insertQuotes = true;
- this._removeOnSubmit = [];
- this._supportPaste = false;
- this._supportPasteOverride = false;
-
- if (elementID) {
- var element = $('#' + elementID);
- if (element.length) {
- this._editorId = elementID;
- this._supportPaste = true;
-
- // get surrounding form-tag
- this._form = element.parents('form:eq(0)');
- if (this._form.length) {
- this._form.submit(this._submit.bind(this));
- this._removeOnSubmit = removeOnSubmit || [];
+ this._proxy.sendRequest();
+
+ // mark element as quoted
+ if ($listItem.data('isQuoted')) {
+ $listItem.data('isQuoted', false).children('a').removeClass('active');
+ }
+ else {
+ $listItem.data('isQuoted', true).children('a').addClass('active');
+ }
+
+ // close navigation on mobile
+ var $navigationList = $listItem.parents('.buttonGroupNavigation');
+ if ($navigationList.hasClass('jsMobileButtonGroupNavigation')) {
+ $navigationList.children('.dropdownLabel').trigger('click');
+ }
+ },
+
+ /**
+ * Saves a quote.
+ *
+ * @param {boolean} renderQuote
+ */
+ _saveQuote: function (renderQuote) {
+ this._proxy.setOption('data', {
+ actionName: 'saveQuote',
+ className: this._className,
+ interfaceName: 'wcf\\data\\IMessageQuoteAction',
+ objectIDs: [this._objectID],
+ parameters: {
+ message: this._message,
+ renderQuote: (renderQuote === true)
}
- else {
- this._form = null;
-
- // allow override
- this._supportPaste = (supportPaste === true);
+ });
+ this._proxy.sendRequest();
+ },
+
+ /**
+ * Saves a quote and directly inserts it.
+ */
+ _saveAndInsertQuote: function () {
+ this._saveQuote(true);
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {Object} data
+ */
+ _success: function (data) {
+ if (data.returnValues.count !== undefined) {
+ if (data.returnValues.fullQuoteMessageIDs !== undefined) {
+ data.returnValues.fullQuoteObjectIDs = data.returnValues.fullQuoteMessageIDs;
}
+
+ var $fullQuoteObjectIDs = (data.returnValues.fullQuoteObjectIDs !== undefined) ? data.returnValues.fullQuoteObjectIDs : {};
+ this._quoteManager.updateCount(data.returnValues.count, $fullQuoteObjectIDs);
+ }
+
+ switch (data.actionName) {
+ case 'saveQuote':
+ case 'saveFullQuote':
+ if (data.returnValues.renderedQuote) {
+ WCF.System.Event.fireEvent('com.woltlab.wcf.message.quote', 'insert', {
+ forceInsert: (data.actionName === 'saveQuote'),
+ quote: data.returnValues.renderedQuote
+ });
+ }
+ break;
+ }
+ },
+
+ /**
+ * Updates the full quote data for all matching objects.
+ *
+ * @param array<integer> $objectIDs
+ */
+ updateFullQuoteObjectIDs: function (objectIDs) {
+ for (var $containerID in this._containers) {
+ this._containers[$containerID].find('.jsQuoteMessage').each(function (index, button) {
+ // reset all markings
+ var $button = $(button).data('isQuoted', 0);
+ $button.children('a').removeClass('active');
+
+ // mark as active
+ if (WCF.inArray($button.data('objectID'), objectIDs)) {
+ $button.data('isQuoted', 1).children('a').addClass('active');
+ }
+ });
}
}
-
- this._proxy = new WCF.Action.Proxy({
- showLoadingOverlay: false,
- success: $.proxy(this._success, this),
- url: 'index.php?message-quote/&t=' + SECURITY_TOKEN
- });
-
- this._toggleShowQuotes();
-
- WCF.System.Event.addListener('com.woltlab.wcf.quote', 'reload', this.countQuotes.bind(this));
-
- // event forwarding
- WCF.System.Event.addListener('com.woltlab.wcf.message.quote', 'insert', (function(data) {
- //noinspection JSUnresolvedVariable
- WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'insertQuote_' + (this._editorIdAlternative ? this._editorIdAlternative : this._editorId), {
- author: data.quote.username,
- content: data.quote.text,
- isText: !data.quote.isFullQuote,
- link: data.quote.link
- });
- }).bind(this));
- },
-
- /**
- * Sets an alternative editor element id on runtime.
- *
- * @param {(string|jQuery)} elementId element id or jQuery element
- */
- setAlternativeEditor: function(elementId) {
- if (!this._editorIdAlternative && !this._supportPaste) {
- this._hasTemplate = false;
- this._supportPaste = true;
- this._supportPasteOverride = true;
- }
-
- if (typeof elementId === 'object') elementId = elementId[0].id;
- this._editorIdAlternative = elementId;
- },
-
- /**
- * Clears alternative editor element id.
- */
- clearAlternativeEditor: function() {
- if (this._supportPasteOverride) {
+ });
+
+ /**
+ * Manages stored quotes.
+ *
+ * @param integer count
+ */
+ WCF.Message.Quote.Manager = Class.extend({
+ /**
+ * list of form buttons
+ * @var {Object}
+ */
+ _buttons: {},
+
+ /**
+ * number of stored quotes
+ * @var {int}
+ */
+ _count: 0,
+
+ /**
+ * dialog overlay
+ * @var {jQuery}
+ */
+ _dialog: null,
+
+ /**
+ * editor element id
+ * @var {string}
+ */
+ _editorId: '',
+
+ /**
+ * alternative editor element id
+ * @var {string}
+ */
+ _editorIdAlternative: '',
+
+ /**
+ * form element
+ * @var {jQuery}
+ */
+ _form: null,
+
+ /**
+ * list of quote handlers
+ * @var {Object}
+ */
+ _handlers: {},
+
+ /**
+ * true, if an up-to-date template exists
+ * @var {boolean}
+ */
+ _hasTemplate: false,
+
+ /**
+ * true, if related quotes should be inserted
+ * @var {boolean}
+ */
+ _insertQuotes: true,
+
+ /**
+ * action proxy
+ * @var {WCF.Action.Proxy}
+ */
+ _proxy: null,
+
+ /**
+ * list of quotes to remove upon submit
+ * @var {Array}
+ */
+ _removeOnSubmit: [],
+
+ /**
+ * allow pasting
+ * @var {boolean}
+ */
+ _supportPaste: false,
+
++ /**
++ * pasting was temporarily enabled due to an alternative editor being set
++ * @var boolean
++ */
++ _supportPasteOverride: false,
++
+ /**
+ * Initializes the quote manager.
+ *
+ * @param {int} count
+ * @param {string} elementID
+ * @param {boolean} supportPaste
+ * @param {Array} removeOnSubmit
+ */
+ init: function (count, elementID, supportPaste, removeOnSubmit) {
+ this._buttons = {
+ insert: null,
+ remove: null
+ };
+ this._count = parseInt(count) || 0;
+ this._dialog = null;
+ this._editorId = '';
+ this._editorIdAlternative = '';
+ this._form = null;
+ this._handlers = {};
this._hasTemplate = false;
+ this._insertQuotes = true;
+ this._removeOnSubmit = [];
this._supportPaste = false;
- }
-
- this._editorIdAlternative = '';
- },
-
- /**
- * Registers a quote handler.
- *
- * @param {string} objectType
- * @param {WCF.Message.Quote.Handler} handler
- */
- register: function(objectType, handler) {
- this._handlers[objectType] = handler;
- },
-
- /**
- * Updates number of stored quotes.
- *
- * @param {int} count
- * @param {Object} fullQuoteObjectIDs
- */
- updateCount: function(count, fullQuoteObjectIDs) {
- this._count = parseInt(count) || 0;
-
- this._toggleShowQuotes();
-
- // update full quote ids of handlers
- for (var $objectType in this._handlers) {
- if (this._handlers.hasOwnProperty($objectType)) {
- var $objectIDs = fullQuoteObjectIDs[$objectType] || [];
- this._handlers[$objectType].updateFullQuoteObjectIDs($objectIDs);
+ this._supportPasteOverride = false;
+
+ if (elementID) {
+ var element = $('#' + elementID);
+ if (element.length) {
+ this._editorId = elementID;
+ this._supportPaste = true;
+
+ // get surrounding form-tag
+ this._form = element.parents('form:eq(0)');
+ if (this._form.length) {
+ this._form.submit(this._submit.bind(this));
+ this._removeOnSubmit = removeOnSubmit || [];
+ }
+ else {
+ this._form = null;
+
+ // allow override
+ this._supportPaste = (supportPaste === true);
+ }
+ }
}
- }
- },
-
- /**
- * Inserts all associated quotes upon first time using quick reply.
- *
- * @param {string} className
- * @param {int} parentObjectID
- * @param {Object} callback
- */
- insertQuotes: function(className, parentObjectID, callback) {
- if (!this._insertQuotes) {
- this._insertQuotes = true;
- return;
- }
-
- new WCF.Action.Proxy({
- autoSend: true,
- data: {
- actionName: 'getRenderedQuotes',
- className: className,
- interfaceName: 'wcf\\data\\IMessageQuoteAction',
- parameters: {
- parentObjectID: parentObjectID
+ this._proxy = new WCF.Action.Proxy({
+ showLoadingOverlay: false,
+ success: $.proxy(this._success, this),
+ url: 'index.php?message-quote/&t=' + SECURITY_TOKEN
+ });
+
+ this._toggleShowQuotes();
+
+ WCF.System.Event.addListener('com.woltlab.wcf.quote', 'reload', this.countQuotes.bind(this));
+
+ // event forwarding
+ WCF.System.Event.addListener('com.woltlab.wcf.message.quote', 'insert', (function (data) {
+ //noinspection JSUnresolvedVariable
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'insertQuote_' + (this._editorIdAlternative ? this._editorIdAlternative : this._editorId), {
+ author: data.quote.username,
+ content: data.quote.text,
+ isText: !data.quote.isFullQuote,
+ link: data.quote.link
+ });
+ }).bind(this));
+ },
+
+ /**
+ * Sets an alternative editor element id on runtime.
+ *
+ * @param {(string|jQuery)} elementId element id or jQuery element
+ */
+ setAlternativeEditor: function (elementId) {
++ if (!this._editorIdAlternative && !this._supportPaste) {
++ this._hasTemplate = false;
++ this._supportPaste = true;
++ this._supportPasteOverride = true;
++ }
++
+ if (typeof elementId === 'object') elementId = elementId[0].id;
+ this._editorIdAlternative = elementId;
+ },
+
+ /**
+ * Clears alternative editor element id.
+ */
+ clearAlternativeEditor: function () {
++ if (this._supportPasteOverride) {
++ this._hasTemplate = false;
++ this._supportPaste = false;
++ this._supportPasteOverride = false;
++ }
++
+ this._editorIdAlternative = '';
+ },
+
+ /**
+ * Registers a quote handler.
+ *
+ * @param {string} objectType
+ * @param {WCF.Message.Quote.Handler} handler
+ */
+ register: function (objectType, handler) {
+ this._handlers[objectType] = handler;
+ },
+
+ /**
+ * Updates number of stored quotes.
+ *
+ * @param {int} count
+ * @param {Object} fullQuoteObjectIDs
+ */
+ updateCount: function (count, fullQuoteObjectIDs) {
+ this._count = parseInt(count) || 0;
+
+ this._toggleShowQuotes();
+
+ // update full quote ids of handlers
+ for (var $objectType in this._handlers) {
+ if (this._handlers.hasOwnProperty($objectType)) {
+ var $objectIDs = fullQuoteObjectIDs[$objectType] || [];
+ this._handlers[$objectType].updateFullQuoteObjectIDs($objectIDs);
}
- },
- success: callback
- });
- },
-
- /**
- * Toggles the display of the 'Show quotes' button
- */
- _toggleShowQuotes: function() {
- require(['WoltLabSuite/Core/Ui/Page/Action'], (function(UiPageAction) {
- var buttonName = 'showQuotes';
-
- if (this._count) {
- var button = UiPageAction.get(buttonName);
- if (button === undefined) {
- button = elCreate('a');
- button.addEventListener('mousedown', this._click.bind(this));
+ }
+ },
+
+ /**
+ * Inserts all associated quotes upon first time using quick reply.
+ *
+ * @param {string} className
+ * @param {int} parentObjectID
+ * @param {Object} callback
+ */
+ insertQuotes: function (className, parentObjectID, callback) {
+ if (!this._insertQuotes) {
+ this._insertQuotes = true;
+
+ return;
+ }
+
+ new WCF.Action.Proxy({
+ autoSend: true,
+ data: {
+ actionName: 'getRenderedQuotes',
+ className: className,
+ interfaceName: 'wcf\\data\\IMessageQuoteAction',
+ parameters: {
+ parentObjectID: parentObjectID
+ }
+ },
+ success: callback
+ });
+ },
+
+ /**
+ * Toggles the display of the 'Show quotes' button
+ */
+ _toggleShowQuotes: function () {
+ require(['WoltLabSuite/Core/Ui/Page/Action'], (function (UiPageAction) {
+ var buttonName = 'showQuotes';
+
+ if (this._count) {
+ var button = UiPageAction.get(buttonName);
+ if (button === undefined) {
+ button = elCreate('a');
+ button.addEventListener('mousedown', this._click.bind(this));
+
+ UiPageAction.add(buttonName, button);
+ }
- UiPageAction.add(buttonName, button);
+ button.textContent = WCF.Language.get('wcf.message.quote.showQuotes').replace(/#count#/, this._count);
+
+ UiPageAction.show(buttonName);
+ }
+ else {
+ UiPageAction.hide(buttonName);
}
- button.textContent = WCF.Language.get('wcf.message.quote.showQuotes').replace(/#count#/, this._count);
+ this._hasTemplate = false;
+ }).bind(this));
+ },
+
+ /**
+ * Handles clicks on 'Show quotes'.
+ */
+ _click: function () {
+ var editor = document.activeElement;
+ if (editor.classList.contains('redactor-layer')) {
+ $('#' + elData(editor, 'element-id')).redactor('selection.save');
+ }
+
+ if (this._hasTemplate) {
+ this._dialog.wcfDialog('open');
+ }
+ else {
+ this._proxy.showLoadingOverlayOnce();
- UiPageAction.show(buttonName);
+ this._proxy.setOption('data', {
+ actionName: 'getQuotes',
+ supportPaste: this._supportPaste
+ });
+ this._proxy.sendRequest();
+ }
+ },
+
+ /**
+ * Renders the dialog.
+ *
+ * @param {string} template
+ */
+ renderDialog: function (template) {
+ // create dialog if not exists
+ if (this._dialog === null) {
+ this._dialog = $('#messageQuoteList');
+ if (!this._dialog.length) {
+ this._dialog = $('<div id="messageQuoteList" />').hide().appendTo(document.body);
+ }
+ }
+
+ // add template
+ this._dialog.html(template);
+
+ // add 'insert' and 'delete' buttons
+ var $formSubmit = $('<div class="formSubmit" />').appendTo(this._dialog);
+ if (this._supportPaste) this._buttons.insert = $('<button class="buttonPrimary">' + WCF.Language.get('wcf.message.quote.insertAllQuotes') + '</button>').click($.proxy(this._insertSelected, this)).appendTo($formSubmit);
+ this._buttons.remove = $('<button>' + WCF.Language.get('wcf.message.quote.removeAllQuotes') + '</button>').click($.proxy(this._removeSelected, this)).appendTo($formSubmit);
+
+ // show dialog
+ this._dialog.wcfDialog({
+ title: WCF.Language.get('wcf.message.quote.manageQuotes')
+ });
+ this._dialog.wcfDialog('render');
+ this._hasTemplate = true;
+
+ // bind event listener
+ var $insertQuoteButtons = this._dialog.find('.jsInsertQuote');
+ if (this._supportPaste) {
+ $insertQuoteButtons.click($.proxy(this._insertQuote, this));
}
else {
- UiPageAction.hide(buttonName);
+ $insertQuoteButtons.hide();
}
- this._hasTemplate = false;
- }).bind(this));
- },
-
- /**
- * Handles clicks on 'Show quotes'.
- */
- _click: function() {
- var editor = document.activeElement;
- if (editor.classList.contains('redactor-layer')) {
- $('#' + elData(editor, 'element-id')).redactor('selection.save');
- }
-
- if (this._hasTemplate) {
- this._dialog.wcfDialog('open');
- }
- else {
- this._proxy.showLoadingOverlayOnce();
+ this._dialog.find('input.jsCheckbox').change($.proxy(this._changeButtons, this));
- this._proxy.setOption('data', {
- actionName: 'getQuotes',
- supportPaste: this._supportPaste
+ // mark quotes for removal
+ if (this._removeOnSubmit.length) {
+ var self = this;
+ this._dialog.find('input.jsRemoveQuote').each(function (index, input) {
+ var $input = $(input).change($.proxy(this._change, this));
+
+ // mark for deletion
+ if (WCF.inArray($input.parent('li').attr('data-quote-id'), self._removeOnSubmit)) {
+ $input.attr('checked', 'checked');
+ }
+ });
+ }
+ },
+
+ /**
+ * Updates button labels if a checkbox is checked or unchecked.
+ */
+ _changeButtons: function () {
+ // selection
+ if (this._dialog.find('input.jsCheckbox:checked').length) {
+ if (this._supportPaste) this._buttons.insert.html(WCF.Language.get('wcf.message.quote.insertSelectedQuotes'));
+ this._buttons.remove.html(WCF.Language.get('wcf.message.quote.removeSelectedQuotes'));
+ }
+ else {
+ // no selection, pick all
+ if (this._supportPaste) this._buttons.insert.html(WCF.Language.get('wcf.message.quote.insertAllQuotes'));
+ this._buttons.remove.html(WCF.Language.get('wcf.message.quote.removeAllQuotes'));
+ }
+ },
+
+ /**
+ * Checks for change event on delete-checkboxes.
+ *
+ * @param {Object} event
+ */
+ _change: function (event) {
+ var $input = $(event.currentTarget);
+ var $quoteID = $input.parent('li').attr('data-quote-id');
+
+ if ($input.prop('checked')) {
+ this._removeOnSubmit.push($quoteID);
+ }
+ else {
+ var index = this._removeOnSubmit.indexOf($quoteID);
+ if (index !== -1) {
+ this._removeOnSubmit.splice(index, 1);
+ }
+ }
+ },
+
+ /**
+ * Inserts the selected quotes.
+ */
+ _insertSelected: function () {
+ if (!this._dialog.find('input.jsCheckbox:checked').length) {
+ this._dialog.find('input.jsCheckbox').prop('checked', 'checked');
+ }
+
+ // insert all quotes
+ this._dialog.find('input.jsCheckbox:checked').each($.proxy(function (index, input) {
+ this._insertQuote(null, input);
+ }, this));
+
+ // close dialog
+ this._dialog.wcfDialog('close');
+ },
+
+ /**
+ * Inserts a quote.
+ *
+ * @param {Event} event
+ * @param {Object} inputElement
+ */
+ _insertQuote: function (event, inputElement) {
+ var listItem = $(event ? event.currentTarget : inputElement).parents('li:eq(0)');
+ var text = listItem.children('.jsFullQuote')[0].textContent.trim();
+
+ var message = listItem.parents('.message:eq(0)');
+ var author = message.data('username');
+ var link = message.data('link');
+ var isText = !elDataBool(listItem[0], 'is-full-quote');
+
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'insertQuote_' + (this._editorIdAlternative ? this._editorIdAlternative : this._editorId), {
+ author: author,
+ content: text,
+ isText: isText,
+ link: link
});
- this._proxy.sendRequest();
- }
- },
-
- /**
- * Renders the dialog.
- *
- * @param {string} template
- */
- renderDialog: function(template) {
- // create dialog if not exists
- if (this._dialog === null) {
- this._dialog = $('#messageQuoteList');
- if (!this._dialog.length) {
- this._dialog = $('<div id="messageQuoteList" />').hide().appendTo(document.body);
+
+ // remove quote upon submit or upon request
+ this._removeOnSubmit.push(listItem.data('quote-id'));
+
+ // close dialog
+ if (event !== null) {
+ this._dialog.wcfDialog('close');
}
- }
-
- // add template
- this._dialog.html(template);
-
- // add 'insert' and 'delete' buttons
- var $formSubmit = $('<div class="formSubmit" />').appendTo(this._dialog);
- if (this._supportPaste) this._buttons.insert = $('<button class="buttonPrimary">' + WCF.Language.get('wcf.message.quote.insertAllQuotes') + '</button>').click($.proxy(this._insertSelected, this)).appendTo($formSubmit);
- this._buttons.remove = $('<button>' + WCF.Language.get('wcf.message.quote.removeAllQuotes') + '</button>').click($.proxy(this._removeSelected, this)).appendTo($formSubmit);
-
- // show dialog
- this._dialog.wcfDialog({
- title: WCF.Language.get('wcf.message.quote.manageQuotes')
- });
- this._dialog.wcfDialog('render');
- this._hasTemplate = true;
-
- // bind event listener
- var $insertQuoteButtons = this._dialog.find('.jsInsertQuote');
- if (this._supportPaste) {
- $insertQuoteButtons.click($.proxy(this._insertQuote, this));
- }
- else {
- $insertQuoteButtons.hide();
- }
-
- this._dialog.find('input.jsCheckbox').change($.proxy(this._changeButtons, this));
-
- // mark quotes for removal
- if (this._removeOnSubmit.length) {
- var self = this;
- this._dialog.find('input.jsRemoveQuote').each(function(index, input) {
- var $input = $(input).change($.proxy(this._change, this));
+ },
+
+ /**
+ * Removes selected quotes.
+ */
+ _removeSelected: function () {
+ if (!this._dialog.find('input.jsCheckbox:checked').length) {
+ this._dialog.find('input.jsCheckbox').prop('checked', 'checked');
+ }
+
+ var $quoteIDs = [];
+ this._dialog.find('input.jsCheckbox:checked').each(function (index, input) {
+ $quoteIDs.push($(input).parents('li').attr('data-quote-id'));
+ });
+
+ if ($quoteIDs.length) {
+ // get object types
+ var $objectTypes = [];
+ for (var $objectType in this._handlers) {
+ if (this._handlers.hasOwnProperty($objectType)) {
+ $objectTypes.push($objectType);
+ }
+ }
+
+ this._proxy.setOption('data', {
+ actionName: 'remove',
+ getFullQuoteObjectIDs: this._handlers.length > 0,
+ objectTypes: $objectTypes,
+ quoteIDs: $quoteIDs
+ });
+ this._proxy.sendRequest();
- // mark for deletion
- if (WCF.inArray($input.parent('li').attr('data-quote-id'), self._removeOnSubmit)) {
- $input.attr('checked', 'checked');
+ this._dialog.wcfDialog('close');
+ }
+ },
+
+ /**
+ * Appends list of quote ids to remove after successful submit.
+ */
+ _submit: function () {
+ if (this._supportPaste && this._removeOnSubmit.length > 0) {
+ var $formSubmit = this._form.find('.formSubmit');
+ for (var i = 0, length = this._removeOnSubmit.length; i < length; i++) {
+ $('<input type="hidden" name="__removeQuoteIDs[]" value="' + this._removeOnSubmit[i] + '" />').appendTo($formSubmit);
}
- });
- }
- },
-
- /**
- * Updates button labels if a checkbox is checked or unchecked.
- */
- _changeButtons: function() {
- // selection
- if (this._dialog.find('input.jsCheckbox:checked').length) {
- if (this._supportPaste) this._buttons.insert.html(WCF.Language.get('wcf.message.quote.insertSelectedQuotes'));
- this._buttons.remove.html(WCF.Language.get('wcf.message.quote.removeSelectedQuotes'));
- }
- else {
- // no selection, pick all
- if (this._supportPaste) this._buttons.insert.html(WCF.Language.get('wcf.message.quote.insertAllQuotes'));
- this._buttons.remove.html(WCF.Language.get('wcf.message.quote.removeAllQuotes'));
- }
- },
-
- /**
- * Checks for change event on delete-checkboxes.
- *
- * @param {Object} event
- */
- _change: function(event) {
- var $input = $(event.currentTarget);
- var $quoteID = $input.parent('li').attr('data-quote-id');
-
- if ($input.prop('checked')) {
- this._removeOnSubmit.push($quoteID);
- }
- else {
- var index = this._removeOnSubmit.indexOf($quoteID);
- if (index !== -1) {
- this._removeOnSubmit.splice(index, 1);
}
- }
- },
-
- /**
- * Inserts the selected quotes.
- */
- _insertSelected: function() {
- if (!this._dialog.find('input.jsCheckbox:checked').length) {
- this._dialog.find('input.jsCheckbox').prop('checked', 'checked');
- }
-
- // insert all quotes
- this._dialog.find('input.jsCheckbox:checked').each($.proxy(function(index, input) {
- this._insertQuote(null, input);
- }, this));
-
- // close dialog
- this._dialog.wcfDialog('close');
- },
-
- /**
- * Inserts a quote.
- *
- * @param {Event} event
- * @param {Object} inputElement
- */
- _insertQuote: function(event, inputElement) {
- var listItem = $(event ? event.currentTarget : inputElement).parents('li:eq(0)');
- var text = listItem.children('.jsFullQuote')[0].textContent.trim();
-
- var message = listItem.parents('.message:eq(0)');
- var author = message.data('username');
- var link = message.data('link');
- var isText = !elDataBool(listItem[0], 'is-full-quote');
-
- WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'insertQuote_' + (this._editorIdAlternative ? this._editorIdAlternative : this._editorId), {
- author: author,
- content: text,
- isText: isText,
- link: link
- });
-
- // remove quote upon submit or upon request
- this._removeOnSubmit.push(listItem.data('quote-id'));
-
- // close dialog
- if (event !== null) {
- this._dialog.wcfDialog('close');
- }
- },
-
- /**
- * Removes selected quotes.
- */
- _removeSelected: function() {
- if (!this._dialog.find('input.jsCheckbox:checked').length) {
- this._dialog.find('input.jsCheckbox').prop('checked', 'checked');
- }
-
- var $quoteIDs = [ ];
- this._dialog.find('input.jsCheckbox:checked').each(function(index, input) {
- $quoteIDs.push($(input).parents('li').attr('data-quote-id'));
- });
+ },
+
+ /**
+ * Returns a list of quote ids marked for removal.
+ *
+ * @return {Array}
+ */
+ getQuotesMarkedForRemoval: function () {
+ return this._removeOnSubmit;
+ },
+
+ /**
+ * Marks quote ids for removal.
+ */
+ markQuotesForRemoval: function () {
+ if (this._removeOnSubmit.length) {
+ this._proxy.setOption('data', {
+ actionName: 'markForRemoval',
+ quoteIDs: this._removeOnSubmit
+ });
+ this._proxy.suppressErrors();
+ this._proxy.sendRequest();
+ }
+ },
+
+ /**
+ * Removes all marked quote ids.
+ */
+ removeMarkedQuotes: function () {
+ if (this._removeOnSubmit.length) {
+ this._proxy.setOption('data', {
+ actionName: 'removeMarkedQuotes',
+ getFullQuoteObjectIDs: this._handlers.length > 0
+ });
+ this._proxy.sendRequest();
+ }
+ },
- if ($quoteIDs.length) {
- // get object types
+ /**
+ * Counts stored quotes.
+ */
+ countQuotes: function () {
var $objectTypes = [];
for (var $objectType in this._handlers) {
if (this._handlers.hasOwnProperty($objectType)) {