/**
* Provides access and editing of message properties.
*
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Ui/Message/Manager
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Manager
*/
-define(['Ajax', 'Core', 'Dictionary', 'Language', 'Dom/ChangeListener', 'Dom/Util'], function (Ajax, Core, Dictionary, Language, DomChangeListener, DomUtil) {
+define(["require", "exports", "tslib", "../../Ajax", "../../Core", "../../Dom/Change/Listener", "../../Language"], function (require, exports, tslib_1, Ajax, Core, Listener_1, Language) {
"use strict";
- if (!COMPILER_TARGET_DEFAULT) {
- var Fake = function () { };
- Fake.prototype = {
- init: function () { },
- rebuild: function () { },
- getPermission: function () { },
- getPropertyValue: function () { },
- update: function () { },
- updateItems: function () { },
- updateAllItems: function () { },
- setNote: function () { },
- _update: function () { },
- _updateState: function () { },
- _toggleMessageStatus: function () { },
- _getAttributeName: function () { },
- _ajaxSuccess: function () { },
- _ajaxSetup: function () { }
- };
- return Fake;
- }
- /**
- * @param {Object} options initialization options
- * @constructor
- */
- function UiMessageManager(options) { this.init(options); }
- UiMessageManager.prototype = {
+ Ajax = tslib_1.__importStar(Ajax);
+ Core = tslib_1.__importStar(Core);
+ Listener_1 = tslib_1.__importDefault(Listener_1);
+ Language = tslib_1.__importStar(Language);
+ class UiMessageManager {
/**
* Initializes a new manager instance.
- *
- * @param {Object} options initialization options
*/
- init: function (options) {
- this._elements = null;
+ constructor(options) {
+ this._elements = new Map();
this._options = Core.extend({
- className: '',
- selector: ''
+ className: "",
+ selector: "",
}, options);
this.rebuild();
- DomChangeListener.add('Ui/Message/Manager' + this._options.className, this.rebuild.bind(this));
- },
+ Listener_1.default.add(`Ui/Message/Manager${this._options.className}`, this.rebuild.bind(this));
+ }
/**
* Rebuilds the list of observed messages. You should call this method whenever a
* message has been either added or removed from the document.
*/
- rebuild: function () {
- this._elements = new Dictionary();
- var element, elements = elBySelAll(this._options.selector);
- for (var i = 0, length = elements.length; i < length; i++) {
- element = elements[i];
- this._elements.set(elData(element, 'object-id'), element);
- }
- },
+ rebuild() {
+ this._elements.clear();
+ document.querySelectorAll(this._options.selector).forEach((element) => {
+ const objectId = ~~(element.dataset.objectId || "0");
+ this._elements.set(objectId, element);
+ });
+ }
/**
* Returns a boolean value for the given permission. The permission should not start
* with "can" or "can-" as this is automatically assumed by this method.
- *
- * @param {int} objectId message object id
- * @param {string} permission permission name without a leading "can" or "can-"
- * @return {boolean} true if permission was set and is either 'true' or '1'
*/
- getPermission: function (objectId, permission) {
- permission = 'can-' + this._getAttributeName(permission);
- var element = this._elements.get(objectId);
+ getPermission(objectId, permission) {
+ permission = "can-" + this._getAttributeName(permission);
+ const element = this._elements.get(objectId);
if (element === undefined) {
- throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ throw new Error(`Unknown object id '${objectId}' for selector '${this._options.selector}'`);
}
- return elDataBool(element, permission);
- },
+ return Core.stringToBool(element.dataset[permission] || "");
+ }
/**
* Returns the given property value from a message, optionally supporting a boolean return value.
- *
- * @param {int} objectId message object id
- * @param {string} propertyName attribute name
- * @param {boolean} asBool attempt to interpret property value as boolean
- * @return {(boolean|string)} raw property value or boolean if requested
*/
- getPropertyValue: function (objectId, propertyName, asBool) {
- var element = this._elements.get(objectId);
+ getPropertyValue(objectId, propertyName, asBool) {
+ const element = this._elements.get(objectId);
if (element === undefined) {
- throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ throw new Error(`Unknown object id '${objectId}' for selector '${this._options.selector}'`);
}
- return window[(asBool ? 'elDataBool' : 'elData')](element, this._getAttributeName(propertyName));
- },
+ const attributeName = this._getAttributeName(propertyName);
+ const value = element.dataset[attributeName] || "";
+ if (asBool) {
+ return Core.stringToBool(value);
+ }
+ return value;
+ }
/**
* Invokes a method for given message object id in order to alter its state or properties.
- *
- * @param {int} objectId message object id
- * @param {string} actionName action name used for the ajax api
- * @param {Object=} parameters optional list of parameters included with the ajax request
*/
- update: function (objectId, actionName, parameters) {
+ update(objectId, actionName, parameters) {
Ajax.api(this, {
actionName: actionName,
parameters: parameters || {},
- objectIDs: [objectId]
+ objectIDs: [objectId],
});
- },
+ }
/**
* Updates properties and states for given object ids. Keep in mind that this method does
* not support setting individual properties per message, instead all property changes
* are applied to all matching message objects.
- *
- * @param {Array<int>} objectIds list of message object ids
- * @param {Object} data list of updated properties
*/
- updateItems: function (objectIds, data) {
+ updateItems(objectIds, data) {
if (!Array.isArray(objectIds)) {
objectIds = [objectIds];
}
- var element;
- for (var i = 0, length = objectIds.length; i < length; i++) {
- element = this._elements.get(objectIds[i]);
+ objectIds.forEach((objectId) => {
+ const element = this._elements.get(objectId);
if (element === undefined) {
- continue;
- }
- for (var key in data) {
- if (data.hasOwnProperty(key)) {
- this._update(element, key, data[key]);
- }
+ return;
}
- }
- },
+ Object.entries(data).forEach(([key, value]) => {
+ this._update(element, key, value);
+ });
+ });
+ }
/**
* Bulk updates the properties and states for all observed messages at once.
- *
- * @param {Object} data list of updated properties
*/
- updateAllItems: function (data) {
- var objectIds = [];
- this._elements.forEach((function (element, objectId) {
- objectIds.push(objectId);
- }).bind(this));
+ updateAllItems(data) {
+ const objectIds = Array.from(this._elements.keys());
this.updateItems(objectIds, data);
- },
+ }
/**
* Sets or removes a message note identified by its unique CSS class.
- *
- * @param {int} objectId message object id
- * @param {string} className unique CSS class
- * @param {string} htmlContent HTML content
*/
- setNote: function (objectId, className, htmlContent) {
- var element = this._elements.get(objectId);
+ setNote(objectId, className, htmlContent) {
+ const element = this._elements.get(objectId);
if (element === undefined) {
- throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ throw new Error(`Unknown object id '${objectId}' for selector '${this._options.selector}'`);
}
- var messageFooterNotes = elBySel('.messageFooterNotes', element);
- var note = elBySel('.' + className, messageFooterNotes);
+ const messageFooterNotes = element.querySelector(".messageFooterNotes");
+ let note = messageFooterNotes.querySelector(`.${className}`);
if (htmlContent) {
if (note === null) {
- note = elCreate('p');
- note.className = 'messageFooterNote ' + className;
+ note = document.createElement("p");
+ note.className = "messageFooterNote " + className;
messageFooterNotes.appendChild(note);
}
note.innerHTML = htmlContent;
}
else if (note !== null) {
- elRemove(note);
+ note.remove();
}
- },
+ }
/**
* Updates a single property of a message element.
- *
- * @param {Element} element message element
- * @param {string} propertyName property name
- * @param {?} propertyValue property value, will be implicitly converted to string
- * @protected
*/
- _update: function (element, propertyName, propertyValue) {
- elData(element, this._getAttributeName(propertyName), propertyValue);
+ _update(element, propertyName, propertyValue) {
+ const attributeName = this._getAttributeName(propertyName);
+ element.dataset[attributeName] = propertyValue.toString();
// handle special properties
- var propertyValueBoolean = (propertyValue == 1 || propertyValue === true || propertyValue === 'true');
+ const propertyValueBoolean = propertyValue == 1 || propertyValue === true || propertyValue === "true";
this._updateState(element, propertyName, propertyValue, propertyValueBoolean);
- },
+ }
/**
* Updates the message element's state based upon a property change.
- *
- * @param {Element} element message element
- * @param {string} propertyName property name
- * @param {?} propertyValue property value
- * @param {boolean} propertyValueBoolean true if `propertyValue` equals either 'true' or '1'
- * @protected
*/
- _updateState: function (element, propertyName, propertyValue, propertyValueBoolean) {
+ _updateState(element, propertyName, propertyValue, propertyValueBoolean) {
switch (propertyName) {
- case 'isDeleted':
- element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDeleted');
- this._toggleMessageStatus(element, 'jsIconDeleted', 'wcf.message.status.deleted', 'red', propertyValueBoolean);
+ case "isDeleted":
+ if (propertyValueBoolean) {
+ element.classList.add("messageDeleted");
+ }
+ else {
+ element.classList.remove("messageDeleted");
+ }
+ this._toggleMessageStatus(element, "jsIconDeleted", "wcf.message.status.deleted", "red", propertyValueBoolean);
break;
- case 'isDisabled':
- element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDisabled');
- this._toggleMessageStatus(element, 'jsIconDisabled', 'wcf.message.status.disabled', 'green', propertyValueBoolean);
+ case "isDisabled":
+ if (propertyValueBoolean) {
+ element.classList.add("messageDisabled");
+ }
+ else {
+ element.classList.remove("messageDisabled");
+ }
+ this._toggleMessageStatus(element, "jsIconDisabled", "wcf.message.status.disabled", "green", propertyValueBoolean);
break;
}
- },
+ }
/**
* Toggles the message status bade for provided element.
- *
- * @param {Element} element message element
- * @param {string} className badge class name
- * @param {string} phrase language phrase
- * @param {string} badgeColor color css class
- * @param {boolean} addBadge add or remove badge
- * @protected
*/
- _toggleMessageStatus: function (element, className, phrase, badgeColor, addBadge) {
- var messageStatus = elBySel('.messageStatus', element);
+ _toggleMessageStatus(element, className, phrase, badgeColor, addBadge) {
+ let messageStatus = element.querySelector(".messageStatus");
if (messageStatus === null) {
- var messageHeaderMetaData = elBySel('.messageHeaderMetaData', element);
+ const messageHeaderMetaData = element.querySelector(".messageHeaderMetaData");
if (messageHeaderMetaData === null) {
// can't find appropriate location to insert badge
return;
}
- messageStatus = elCreate('ul');
- messageStatus.className = 'messageStatus';
- DomUtil.insertAfter(messageStatus, messageHeaderMetaData);
+ messageStatus = document.createElement("ul");
+ messageStatus.className = "messageStatus";
+ messageHeaderMetaData.insertAdjacentElement("afterend", messageStatus);
}
- var badge = elBySel('.' + className, messageStatus);
+ let badge = messageStatus.querySelector(`.${className}`);
if (addBadge) {
if (badge !== null) {
// badge already exists
return;
}
- badge = elCreate('span');
- badge.className = 'badge label ' + badgeColor + ' ' + className;
+ badge = document.createElement("span");
+ badge.className = `badge label ${badgeColor} ${className}`;
badge.textContent = Language.get(phrase);
- var listItem = elCreate('li');
+ const listItem = document.createElement("li");
listItem.appendChild(badge);
messageStatus.appendChild(listItem);
}
// badge does not exist
return;
}
- elRemove(badge.parentNode);
+ badge.parentElement.remove();
}
- },
+ }
/**
* Transforms camel-cased property names into their attribute equivalent.
- *
- * @param {string} propertyName camel-cased property name
- * @return {string} equivalent attribute name
- * @protected
*/
- _getAttributeName: function (propertyName) {
- if (propertyName.indexOf('-') !== -1) {
+ _getAttributeName(propertyName) {
+ if (propertyName.indexOf("-") !== -1) {
return propertyName;
}
- var attributeName = '';
- var str, tmp = propertyName.split(/([A-Z][a-z]+)/);
- for (var i = 0, length = tmp.length; i < length; i++) {
- str = tmp[i];
- if (str.length) {
- if (attributeName.length)
- attributeName += '-';
- attributeName += str.toLowerCase();
- }
- }
- return attributeName;
- },
- _ajaxSuccess: function () {
+ return propertyName
+ .split(/([A-Z][a-z]+)/)
+ .map((s) => s.trim().toLowerCase())
+ .filter((s) => s.length > 0)
+ .join("-");
+ }
+ _ajaxSuccess(_data) {
+ // This should be an abstract method, but cannot be marked as such for backwards compatibility.
throw new Error("Method _ajaxSuccess() must be implemented by deriving functions.");
- },
- _ajaxSetup: function () {
+ }
+ _ajaxSetup() {
return {
data: {
- className: this._options.className
- }
+ className: this._options.className,
+ },
};
}
- };
+ }
+ Core.enableLegacyInheritance(UiMessageManager);
return UiMessageManager;
});
+++ /dev/null
-/**
- * Provides access and editing of message properties.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Ui/Message/Manager
- */
-define(['Ajax', 'Core', 'Dictionary', 'Language', 'Dom/ChangeListener', 'Dom/Util'], function(Ajax, Core, Dictionary, Language, DomChangeListener, DomUtil) {
- "use strict";
-
- if (!COMPILER_TARGET_DEFAULT) {
- var Fake = function() {};
- Fake.prototype = {
- init: function() {},
- rebuild: function() {},
- getPermission: function() {},
- getPropertyValue: function() {},
- update: function() {},
- updateItems: function() {},
- updateAllItems: function() {},
- setNote: function() {},
- _update: function() {},
- _updateState: function() {},
- _toggleMessageStatus: function() {},
- _getAttributeName: function() {},
- _ajaxSuccess: function() {},
- _ajaxSetup: function() {}
- };
- return Fake;
- }
-
- /**
- * @param {Object} options initialization options
- * @constructor
- */
- function UiMessageManager(options) { this.init(options); }
- UiMessageManager.prototype = {
- /**
- * Initializes a new manager instance.
- *
- * @param {Object} options initialization options
- */
- init: function(options) {
- this._elements = null;
- this._options = Core.extend({
- className: '',
- selector: ''
- }, options);
-
- this.rebuild();
-
- DomChangeListener.add('Ui/Message/Manager' + this._options.className, this.rebuild.bind(this));
- },
-
- /**
- * Rebuilds the list of observed messages. You should call this method whenever a
- * message has been either added or removed from the document.
- */
- rebuild: function() {
- this._elements = new Dictionary();
-
- var element, elements = elBySelAll(this._options.selector);
- for (var i = 0, length = elements.length; i < length; i++) {
- element = elements[i];
-
- this._elements.set(elData(element, 'object-id'), element);
- }
- },
-
- /**
- * Returns a boolean value for the given permission. The permission should not start
- * with "can" or "can-" as this is automatically assumed by this method.
- *
- * @param {int} objectId message object id
- * @param {string} permission permission name without a leading "can" or "can-"
- * @return {boolean} true if permission was set and is either 'true' or '1'
- */
- getPermission: function(objectId, permission) {
- permission = 'can-' + this._getAttributeName(permission);
- var element = this._elements.get(objectId);
- if (element === undefined) {
- throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
- }
-
- return elDataBool(element, permission);
- },
-
- /**
- * Returns the given property value from a message, optionally supporting a boolean return value.
- *
- * @param {int} objectId message object id
- * @param {string} propertyName attribute name
- * @param {boolean} asBool attempt to interpret property value as boolean
- * @return {(boolean|string)} raw property value or boolean if requested
- */
- getPropertyValue: function(objectId, propertyName, asBool) {
- var element = this._elements.get(objectId);
- if (element === undefined) {
- throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
- }
-
- return window[(asBool ? 'elDataBool' : 'elData')](element, this._getAttributeName(propertyName));
- },
-
- /**
- * Invokes a method for given message object id in order to alter its state or properties.
- *
- * @param {int} objectId message object id
- * @param {string} actionName action name used for the ajax api
- * @param {Object=} parameters optional list of parameters included with the ajax request
- */
- update: function(objectId, actionName, parameters) {
- Ajax.api(this, {
- actionName: actionName,
- parameters: parameters || {},
- objectIDs: [objectId]
- });
- },
-
- /**
- * Updates properties and states for given object ids. Keep in mind that this method does
- * not support setting individual properties per message, instead all property changes
- * are applied to all matching message objects.
- *
- * @param {Array<int>} objectIds list of message object ids
- * @param {Object} data list of updated properties
- */
- updateItems: function(objectIds, data) {
- if (!Array.isArray(objectIds)) {
- objectIds = [objectIds];
- }
-
- var element;
- for (var i = 0, length = objectIds.length; i < length; i++) {
- element = this._elements.get(objectIds[i]);
- if (element === undefined) {
- continue;
- }
-
- for (var key in data) {
- if (data.hasOwnProperty(key)) {
- this._update(element, key, data[key]);
- }
- }
- }
- },
-
- /**
- * Bulk updates the properties and states for all observed messages at once.
- *
- * @param {Object} data list of updated properties
- */
- updateAllItems: function(data) {
- var objectIds = [];
- this._elements.forEach((function(element, objectId) {
- objectIds.push(objectId);
- }).bind(this));
-
- this.updateItems(objectIds, data);
- },
-
- /**
- * Sets or removes a message note identified by its unique CSS class.
- *
- * @param {int} objectId message object id
- * @param {string} className unique CSS class
- * @param {string} htmlContent HTML content
- */
- setNote: function (objectId, className, htmlContent) {
- var element = this._elements.get(objectId);
- if (element === undefined) {
- throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
- }
-
- var messageFooterNotes = elBySel('.messageFooterNotes', element);
- var note = elBySel('.' + className, messageFooterNotes);
- if (htmlContent) {
- if (note === null) {
- note = elCreate('p');
- note.className = 'messageFooterNote ' + className;
-
- messageFooterNotes.appendChild(note);
- }
-
- note.innerHTML = htmlContent;
- }
- else if (note !== null) {
- elRemove(note);
- }
- },
-
- /**
- * Updates a single property of a message element.
- *
- * @param {Element} element message element
- * @param {string} propertyName property name
- * @param {?} propertyValue property value, will be implicitly converted to string
- * @protected
- */
- _update: function(element, propertyName, propertyValue) {
- elData(element, this._getAttributeName(propertyName), propertyValue);
-
- // handle special properties
- var propertyValueBoolean = (propertyValue == 1 || propertyValue === true || propertyValue === 'true');
- this._updateState(element, propertyName, propertyValue, propertyValueBoolean);
- },
-
- /**
- * Updates the message element's state based upon a property change.
- *
- * @param {Element} element message element
- * @param {string} propertyName property name
- * @param {?} propertyValue property value
- * @param {boolean} propertyValueBoolean true if `propertyValue` equals either 'true' or '1'
- * @protected
- */
- _updateState: function(element, propertyName, propertyValue, propertyValueBoolean) {
- switch (propertyName) {
- case 'isDeleted':
- element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDeleted');
- this._toggleMessageStatus(element, 'jsIconDeleted', 'wcf.message.status.deleted', 'red', propertyValueBoolean);
-
- break;
-
- case 'isDisabled':
- element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDisabled');
- this._toggleMessageStatus(element, 'jsIconDisabled', 'wcf.message.status.disabled', 'green', propertyValueBoolean);
-
- break;
- }
- },
-
- /**
- * Toggles the message status bade for provided element.
- *
- * @param {Element} element message element
- * @param {string} className badge class name
- * @param {string} phrase language phrase
- * @param {string} badgeColor color css class
- * @param {boolean} addBadge add or remove badge
- * @protected
- */
- _toggleMessageStatus: function(element, className, phrase, badgeColor, addBadge) {
- var messageStatus = elBySel('.messageStatus', element);
- if (messageStatus === null) {
- var messageHeaderMetaData = elBySel('.messageHeaderMetaData', element);
- if (messageHeaderMetaData === null) {
- // can't find appropriate location to insert badge
- return;
- }
-
- messageStatus = elCreate('ul');
- messageStatus.className = 'messageStatus';
- DomUtil.insertAfter(messageStatus, messageHeaderMetaData);
- }
-
- var badge = elBySel('.' + className, messageStatus);
-
- if (addBadge) {
- if (badge !== null) {
- // badge already exists
- return;
- }
-
- badge = elCreate('span');
- badge.className = 'badge label ' + badgeColor + ' ' + className;
- badge.textContent = Language.get(phrase);
-
- var listItem = elCreate('li');
- listItem.appendChild(badge);
- messageStatus.appendChild(listItem);
- }
- else {
- if (badge === null) {
- // badge does not exist
- return;
- }
-
- elRemove(badge.parentNode);
- }
- },
-
- /**
- * Transforms camel-cased property names into their attribute equivalent.
- *
- * @param {string} propertyName camel-cased property name
- * @return {string} equivalent attribute name
- * @protected
- */
- _getAttributeName: function(propertyName) {
- if (propertyName.indexOf('-') !== -1) {
- return propertyName;
- }
-
- var attributeName = '';
- var str, tmp = propertyName.split(/([A-Z][a-z]+)/);
- for (var i = 0, length = tmp.length; i < length; i++) {
- str = tmp[i];
- if (str.length) {
- if (attributeName.length) attributeName += '-';
- attributeName += str.toLowerCase();
- }
- }
-
- return attributeName;
- },
-
- _ajaxSuccess: function() {
- throw new Error("Method _ajaxSuccess() must be implemented by deriving functions.");
- },
-
- _ajaxSetup: function() {
- return {
- data: {
- className: this._options.className
- }
- };
- }
- };
-
- return UiMessageManager;
-});
\ No newline at end of file
--- /dev/null
+/**
+ * Provides access and editing of message properties.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Manager
+ */
+
+import * as Ajax from "../../Ajax";
+import { AjaxCallbackObject, AjaxCallbackSetup, ResponseData } from "../../Ajax/Data";
+import * as Core from "../../Core";
+import DomChangeListener from "../../Dom/Change/Listener";
+import * as Language from "../../Language";
+
+interface MessageManagerOptions {
+ className: string;
+ selector: string;
+}
+
+type StringableValue = boolean | number | string;
+
+class UiMessageManager implements AjaxCallbackObject {
+ protected readonly _elements = new Map<number, HTMLElement>();
+ protected readonly _options: MessageManagerOptions;
+
+ /**
+ * Initializes a new manager instance.
+ */
+ constructor(options: MessageManagerOptions) {
+ this._options = Core.extend(
+ {
+ className: "",
+ selector: "",
+ },
+ options,
+ ) as MessageManagerOptions;
+
+ this.rebuild();
+
+ DomChangeListener.add(`Ui/Message/Manager${this._options.className}`, this.rebuild.bind(this));
+ }
+
+ /**
+ * Rebuilds the list of observed messages. You should call this method whenever a
+ * message has been either added or removed from the document.
+ */
+ rebuild(): void {
+ this._elements.clear();
+
+ document.querySelectorAll(this._options.selector).forEach((element: HTMLElement) => {
+ const objectId = ~~(element.dataset.objectId || "0");
+ this._elements.set(objectId, element);
+ });
+ }
+
+ /**
+ * Returns a boolean value for the given permission. The permission should not start
+ * with "can" or "can-" as this is automatically assumed by this method.
+ */
+ getPermission(objectId: number, permission: string): boolean {
+ permission = "can-" + this._getAttributeName(permission);
+ const element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error(`Unknown object id '${objectId}' for selector '${this._options.selector}'`);
+ }
+
+ return Core.stringToBool(element.dataset[permission] || "");
+ }
+
+ /**
+ * Returns the given property value from a message, optionally supporting a boolean return value.
+ */
+ getPropertyValue(objectId: number, propertyName: string, asBool: boolean): boolean | string {
+ const element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error(`Unknown object id '${objectId}' for selector '${this._options.selector}'`);
+ }
+
+ const attributeName = this._getAttributeName(propertyName);
+ const value = element.dataset[attributeName] || "";
+
+ if (asBool) {
+ return Core.stringToBool(value);
+ }
+
+ return value;
+ }
+
+ /**
+ * Invokes a method for given message object id in order to alter its state or properties.
+ */
+ update(objectId: number, actionName: string, parameters?: ArbitraryObject): void {
+ Ajax.api(this, {
+ actionName: actionName,
+ parameters: parameters || {},
+ objectIDs: [objectId],
+ });
+ }
+
+ /**
+ * Updates properties and states for given object ids. Keep in mind that this method does
+ * not support setting individual properties per message, instead all property changes
+ * are applied to all matching message objects.
+ */
+ updateItems(objectIds: number | number[], data: ArbitraryObject): void {
+ if (!Array.isArray(objectIds)) {
+ objectIds = [objectIds];
+ }
+
+ objectIds.forEach((objectId) => {
+ const element = this._elements.get(objectId);
+ if (element === undefined) {
+ return;
+ }
+
+ Object.entries(data).forEach(([key, value]) => {
+ this._update(element, key, value as StringableValue);
+ });
+ });
+ }
+
+ /**
+ * Bulk updates the properties and states for all observed messages at once.
+ */
+ updateAllItems(data: ArbitraryObject): void {
+ const objectIds = Array.from(this._elements.keys());
+
+ this.updateItems(objectIds, data);
+ }
+
+ /**
+ * Sets or removes a message note identified by its unique CSS class.
+ */
+ setNote(objectId: number, className: string, htmlContent: string): void {
+ const element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error(`Unknown object id '${objectId}' for selector '${this._options.selector}'`);
+ }
+
+ const messageFooterNotes = element.querySelector(".messageFooterNotes") as HTMLElement;
+ let note = messageFooterNotes.querySelector(`.${className}`);
+ if (htmlContent) {
+ if (note === null) {
+ note = document.createElement("p");
+ note.className = "messageFooterNote " + className;
+
+ messageFooterNotes.appendChild(note);
+ }
+
+ note.innerHTML = htmlContent;
+ } else if (note !== null) {
+ note.remove();
+ }
+ }
+
+ /**
+ * Updates a single property of a message element.
+ */
+ protected _update(element: HTMLElement, propertyName: string, propertyValue: StringableValue): void {
+ const attributeName = this._getAttributeName(propertyName);
+ element.dataset[attributeName] = propertyValue.toString();
+
+ // handle special properties
+ const propertyValueBoolean = propertyValue == 1 || propertyValue === true || propertyValue === "true";
+ this._updateState(element, propertyName, propertyValue, propertyValueBoolean);
+ }
+
+ /**
+ * Updates the message element's state based upon a property change.
+ */
+ protected _updateState(
+ element: HTMLElement,
+ propertyName: string,
+ propertyValue: StringableValue,
+ propertyValueBoolean: boolean,
+ ): void {
+ switch (propertyName) {
+ case "isDeleted":
+ if (propertyValueBoolean) {
+ element.classList.add("messageDeleted");
+ } else {
+ element.classList.remove("messageDeleted");
+ }
+
+ this._toggleMessageStatus(element, "jsIconDeleted", "wcf.message.status.deleted", "red", propertyValueBoolean);
+
+ break;
+
+ case "isDisabled":
+ if (propertyValueBoolean) {
+ element.classList.add("messageDisabled");
+ } else {
+ element.classList.remove("messageDisabled");
+ }
+
+ this._toggleMessageStatus(
+ element,
+ "jsIconDisabled",
+ "wcf.message.status.disabled",
+ "green",
+ propertyValueBoolean,
+ );
+
+ break;
+ }
+ }
+
+ /**
+ * Toggles the message status bade for provided element.
+ */
+ protected _toggleMessageStatus(
+ element: HTMLElement,
+ className: string,
+ phrase: string,
+ badgeColor: string,
+ addBadge: boolean,
+ ): void {
+ let messageStatus = element.querySelector(".messageStatus");
+ if (messageStatus === null) {
+ const messageHeaderMetaData = element.querySelector(".messageHeaderMetaData");
+ if (messageHeaderMetaData === null) {
+ // can't find appropriate location to insert badge
+ return;
+ }
+
+ messageStatus = document.createElement("ul");
+ messageStatus.className = "messageStatus";
+ messageHeaderMetaData.insertAdjacentElement("afterend", messageStatus);
+ }
+
+ let badge = messageStatus.querySelector(`.${className}`);
+ if (addBadge) {
+ if (badge !== null) {
+ // badge already exists
+ return;
+ }
+
+ badge = document.createElement("span");
+ badge.className = `badge label ${badgeColor} ${className}`;
+ badge.textContent = Language.get(phrase);
+
+ const listItem = document.createElement("li");
+ listItem.appendChild(badge);
+ messageStatus.appendChild(listItem);
+ } else {
+ if (badge === null) {
+ // badge does not exist
+ return;
+ }
+
+ badge.parentElement!.remove();
+ }
+ }
+
+ /**
+ * Transforms camel-cased property names into their attribute equivalent.
+ */
+ protected _getAttributeName(propertyName: string): string {
+ if (propertyName.indexOf("-") !== -1) {
+ return propertyName;
+ }
+
+ return propertyName
+ .split(/([A-Z][a-z]+)/)
+ .map((s) => s.trim().toLowerCase())
+ .filter((s) => s.length > 0)
+ .join("-");
+ }
+
+ _ajaxSuccess(_data: ResponseData): void {
+ // This should be an abstract method, but cannot be marked as such for backwards compatibility.
+ throw new Error("Method _ajaxSuccess() must be implemented by deriving functions.");
+ }
+
+ _ajaxSetup(): ReturnType<AjaxCallbackSetup> {
+ return {
+ data: {
+ className: this._options.className,
+ },
+ };
+ }
+}
+
+Core.enableLegacyInheritance(UiMessageManager);
+
+export = UiMessageManager;