/**
* Clipboard API Handler.
*
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Controller/Clipboard
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Clipboard
*/
-define([
- 'Ajax', 'Core', 'Dictionary', 'EventHandler',
- 'Language', 'List', 'ObjectMap', 'Dom/ChangeListener',
- 'Dom/Traverse', 'Dom/Util', 'Ui/Confirmation', 'Ui/SimpleDropdown',
- 'WoltLabSuite/Core/Ui/Page/Action', 'Ui/Screen'
-], function (Ajax, Core, Dictionary, EventHandler, Language, List, ObjectMap, DomChangeListener, DomTraverse, DomUtil, UiConfirmation, UiSimpleDropdown, UiPageAction, UiScreen) {
+define(["require", "exports", "tslib", "../Ajax", "../Core", "../Dom/Change/Listener", "../Dom/Util", "../Event/Handler", "../Language", "../Ui/Confirmation", "../Ui/Dropdown/Simple", "../Ui/Page/Action", "../Ui/Screen"], function (require, exports, tslib_1, Ajax, Core, Listener_1, Util_1, EventHandler, Language, UiConfirmation, Simple_1, UiPageAction, UiScreen) {
"use strict";
- if (!COMPILER_TARGET_DEFAULT) {
- return {
- setup: function () { },
- reload: function () { },
- _initContainers: function () { },
- _loadMarkedItems: function () { },
- _markAll: function () { },
- _mark: function () { },
- _saveState: function () { },
- _executeAction: function () { },
- _executeProxyAction: function () { },
- _unmarkAll: function () { },
- _ajaxSetup: function () { },
- _ajaxSuccess: function () { },
- _rebuildMarkings: function () { },
- hideEditor: function () { },
- showEditor: function () { },
- unmark: function () { }
- };
- }
- var _containers = new Dictionary();
- var _editors = new Dictionary();
- var _editorDropdowns = new Dictionary();
- var _elements = elByClass('jsClipboardContainer');
- var _itemData = new ObjectMap();
- var _knownCheckboxes = new List();
- var _options = {};
- var _reloadPageOnSuccess = new Dictionary();
- var _callbackCheckbox = null;
- var _callbackItem = null;
- var _callbackUnmarkAll = null;
- var _specialCheckboxSelector = '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]';
- /**
- * Clipboard API
- *
- * @exports WoltLabSuite/Core/Controller/Clipboard
- */
- return {
+ Object.defineProperty(exports, "__esModule", { value: true });
+ exports.unmark = exports.showEditor = exports.hideEditor = exports.reload = exports.setup = void 0;
+ Ajax = tslib_1.__importStar(Ajax);
+ Core = tslib_1.__importStar(Core);
+ Listener_1 = tslib_1.__importDefault(Listener_1);
+ Util_1 = tslib_1.__importDefault(Util_1);
+ EventHandler = tslib_1.__importStar(EventHandler);
+ Language = tslib_1.__importStar(Language);
+ UiConfirmation = tslib_1.__importStar(UiConfirmation);
+ Simple_1 = tslib_1.__importDefault(Simple_1);
+ UiPageAction = tslib_1.__importStar(UiPageAction);
+ UiScreen = tslib_1.__importStar(UiScreen);
+ const _specialCheckboxSelector = '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]';
+ class ControllerClipboard {
+ constructor() {
+ this.containers = new Map();
+ this.editors = new Map();
+ this.editorDropdowns = new Map();
+ this.itemData = new WeakMap();
+ this.knownCheckboxes = new WeakSet();
+ this.pageClassNames = [];
+ this.pageObjectId = 0;
+ this.reloadPageOnSuccess = new Map();
+ }
/**
* Initializes the clipboard API handler.
- *
- * @param {Object} options initialization options
*/
- setup: function (options) {
+ setup(options) {
if (!options.pageClassName) {
throw new Error("Expected a non-empty string for parameter 'pageClassName'.");
}
- if (_callbackCheckbox === null) {
- _callbackCheckbox = this._mark.bind(this);
- _callbackItem = this._executeAction.bind(this);
- _callbackUnmarkAll = this._unmarkAll.bind(this);
- _options = Core.extend({
- hasMarkedItems: false,
- pageClassNames: [options.pageClassName],
- pageObjectId: 0
- }, options);
- delete _options.pageClassName;
- }
- else {
- if (options.pageObjectId) {
- throw new Error("Cannot load secondary clipboard with page object id set.");
- }
- _options.pageClassNames.push(options.pageClassName);
- }
- if (!Element.prototype.matches) {
- Element.prototype.matches = Element.prototype.msMatchesSelector;
+ let hasMarkedItems = false;
+ if (this.pageClassNames.length === 0) {
+ hasMarkedItems = options.hasMarkedItems;
+ this.pageObjectId = options.pageObjectId;
}
- this._initContainers();
- if (_options.hasMarkedItems && _elements.length) {
- this._loadMarkedItems();
+ this.pageClassNames.push(options.pageClassName);
+ this.initContainers();
+ if (hasMarkedItems && this.containers.size) {
+ this.loadMarkedItems();
}
- DomChangeListener.add('WoltLabSuite/Core/Controller/Clipboard', this._initContainers.bind(this));
- },
+ Listener_1.default.add("WoltLabSuite/Core/Controller/Clipboard", () => this.initContainers());
+ }
/**
* Reloads the clipboard data.
*/
- reload: function () {
- if (_containers.size) {
- this._loadMarkedItems();
+ reload() {
+ if (this.containers.size) {
+ this.loadMarkedItems();
}
- },
+ }
/**
* Initializes clipboard containers.
*/
- _initContainers: function () {
- for (var i = 0, length = _elements.length; i < length; i++) {
- var container = _elements[i];
- var containerId = DomUtil.identify(container);
- var containerData = _containers.get(containerId);
+ initContainers() {
+ document.querySelectorAll(".jsClipboardContainer").forEach((container) => {
+ const containerId = Util_1.default.identify(container);
+ let containerData = this.containers.get(containerId);
if (containerData === undefined) {
- var markAll = elBySel('.jsClipboardMarkAll', container);
+ const markAll = container.querySelector(".jsClipboardMarkAll");
if (markAll !== null) {
if (markAll.matches(_specialCheckboxSelector)) {
- var label = markAll.closest('label');
- elAttr(label, 'role', 'checkbox');
- elAttr(label, 'tabindex', '0');
- elAttr(label, 'aria-checked', false);
- elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.markAll'));
- label.addEventListener('keyup', function (event) {
- if (event.keyCode === 13 || event.keyCode === 32) {
- checkbox.click();
+ const label = markAll.closest("label");
+ label.setAttribute("role", "checkbox");
+ label.tabIndex = 0;
+ label.setAttribute("aria-checked", "false");
+ label.setAttribute("aria-label", Language.get("wcf.clipboard.item.markAll"));
+ label.addEventListener("keyup", (event) => {
+ if (event.key === "Enter" || event.key === "Space") {
+ markAll.click();
}
});
}
- elData(markAll, 'container-id', containerId);
- markAll.addEventListener('click', this._markAll.bind(this));
+ markAll.dataset.containerId = containerId;
+ markAll.addEventListener("click", (ev) => this.markAll(ev));
}
containerData = {
- checkboxes: elByClass('jsClipboardItem', container),
+ checkboxes: container.getElementsByClassName("jsClipboardItem"),
element: container,
markAll: markAll,
- markedObjectIds: new List()
+ markedObjectIds: new Set(),
};
- _containers.set(containerId, containerData);
+ this.containers.set(containerId, containerData);
}
- for (var j = 0, innerLength = containerData.checkboxes.length; j < innerLength; j++) {
- var checkbox = containerData.checkboxes[j];
- if (!_knownCheckboxes.has(checkbox)) {
- elData(checkbox, 'container-id', containerId);
- (function (checkbox) {
- if (checkbox.matches(_specialCheckboxSelector)) {
- var label = checkbox.closest('label');
- elAttr(label, 'role', 'checkbox');
- elAttr(label, 'tabindex', '0');
- elAttr(label, 'aria-checked', false);
- elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.mark'));
- label.addEventListener('keyup', function (event) {
- if (event.keyCode === 13 || event.keyCode === 32) {
- checkbox.click();
- }
- });
- }
- var link = checkbox.closest('a');
- if (link === null) {
- checkbox.addEventListener('click', _callbackCheckbox);
- }
- else {
- // Firefox will always trigger the link if the checkbox is
- // inside of one. Since 2000. Thanks Firefox.
- checkbox.addEventListener('click', function (event) {
- event.preventDefault();
- window.setTimeout(function () {
- checkbox.checked = !checkbox.checked;
- _callbackCheckbox(null, checkbox);
- }, 10);
- });
+ Array.from(containerData.checkboxes).forEach((checkbox) => {
+ if (this.knownCheckboxes.has(checkbox)) {
+ return;
+ }
+ checkbox.dataset.containerId = containerId;
+ if (checkbox.matches(_specialCheckboxSelector)) {
+ const label = checkbox.closest("label");
+ label.setAttribute("role", "checkbox");
+ label.tabIndex = 0;
+ label.setAttribute("aria-checked", "false");
+ label.setAttribute("aria-label", Language.get("wcf.clipboard.item.mark"));
+ label.addEventListener("keyup", (event) => {
+ if (event.key === "Enter" || event.key === "Space") {
+ checkbox.click();
}
- })(checkbox);
- _knownCheckboxes.add(checkbox);
+ });
}
- }
- }
- },
+ const link = checkbox.closest("a");
+ if (link === null) {
+ checkbox.addEventListener("click", (ev) => this.mark(ev));
+ }
+ else {
+ // Firefox will always trigger the link if the checkbox is
+ // inside of one. Since 2000. Thanks Firefox.
+ checkbox.addEventListener("click", (event) => {
+ event.preventDefault();
+ window.setTimeout(() => {
+ checkbox.checked = !checkbox.checked;
+ this.mark(checkbox);
+ }, 10);
+ });
+ }
+ this.knownCheckboxes.add(checkbox);
+ });
+ });
+ }
/**
* Loads marked items from clipboard.
*/
- _loadMarkedItems: function () {
+ loadMarkedItems() {
Ajax.api(this, {
- actionName: 'getMarkedItems',
+ actionName: "getMarkedItems",
parameters: {
- pageClassNames: _options.pageClassNames,
- pageObjectID: _options.pageObjectId
- }
+ pageClassNames: this.pageClassNames,
+ pageObjectID: this.pageObjectId,
+ },
});
- },
+ }
/**
* Marks or unmarks all visible items at once.
- *
- * @param {object} event event object
*/
- _markAll: function (event) {
- var checkbox = event.currentTarget;
- var isMarked = (checkbox.nodeName !== 'INPUT' || checkbox.checked);
- if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
- elAttr(checkbox.parentNode, 'aria-checked', isMarked);
- }
- var objectIds = [];
- var containerId = elData(checkbox, 'container-id');
- var data = _containers.get(containerId);
- var type = elData(data.element, 'type');
- for (var i = 0, length = data.checkboxes.length; i < length; i++) {
- var item = data.checkboxes[i];
- var objectId = ~~elData(item, 'object-id');
+ markAll(event) {
+ const checkbox = event.currentTarget;
+ const isMarked = checkbox.nodeName !== "INPUT" || checkbox.checked;
+ this.setParentAsMarked(checkbox, isMarked);
+ const objectIds = [];
+ const containerId = checkbox.dataset.containerId;
+ const data = this.containers.get(containerId);
+ const type = data.element.dataset.type;
+ Array.from(data.checkboxes).forEach((item) => {
+ const objectId = ~~item.dataset.objectId;
if (isMarked) {
if (!item.checked) {
item.checked = true;
else {
if (item.checked) {
item.checked = false;
- data.markedObjectIds['delete'](objectId);
+ data.markedObjectIds["delete"](objectId);
objectIds.push(objectId);
}
}
- if (elAttr(item.parentNode, 'role') === 'checkbox') {
- elAttr(item.parentNode, 'aria-checked', isMarked);
- }
- var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
+ this.setParentAsMarked(item, isMarked);
+ const clipboardObject = checkbox.closest(".jsClipboardObject");
if (clipboardObject !== null) {
- clipboardObject.classList[(isMarked ? 'addClass' : 'removeClass')]('jsMarked');
+ if (isMarked) {
+ clipboardObject.classList.add("jsMarked");
+ }
+ else {
+ clipboardObject.classList.remove("jsMarked");
+ }
}
- }
- this._saveState(type, objectIds, isMarked);
- },
+ });
+ this.saveState(type, objectIds, isMarked);
+ }
/**
* Marks or unmarks an individual item.
*
- * @param {object} event event object
- * @param {Element=} checkbox checkbox element
*/
- _mark: function (event, checkbox) {
- checkbox = (event instanceof Event) ? event.currentTarget : checkbox;
- var objectId = ~~elData(checkbox, 'object-id');
- var isMarked = checkbox.checked;
- var containerId = elData(checkbox, 'container-id');
- var data = _containers.get(containerId);
- var type = elData(data.element, 'type');
- var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
- data.markedObjectIds[(isMarked ? 'add' : 'delete')](objectId);
- clipboardObject.classList[(isMarked) ? 'add' : 'remove']('jsMarked');
- if (data.markAll !== null) {
- var markedAll = true;
- for (var i = 0, length = data.checkboxes.length; i < length; i++) {
- if (!data.checkboxes[i].checked) {
- markedAll = false;
- break;
- }
- }
- data.markAll.checked = markedAll;
- if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
- elAttr(data.markAll.parentNode, 'aria-checked', isMarked);
- }
+ mark(event) {
+ const checkbox = event instanceof Event ? event.currentTarget : event;
+ const objectId = ~~checkbox.dataset.objectId;
+ const isMarked = checkbox.checked;
+ const containerId = checkbox.dataset.containerId;
+ const data = this.containers.get(containerId);
+ const type = data.element.dataset.type;
+ const clipboardObject = checkbox.closest(".jsClipboardObject");
+ if (isMarked) {
+ data.markedObjectIds.add(objectId);
+ clipboardObject.classList.add("jsMarked");
+ }
+ else {
+ data.markedObjectIds.delete(objectId);
+ clipboardObject.classList.remove("jsMarked");
}
- if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
- elAttr(checkbox.parentNode, 'aria-checked', checkbox.checked);
+ if (data.markAll !== null) {
+ data.markAll.checked = Array.from(data.checkboxes).some((item) => !item.checked);
+ this.setParentAsMarked(data.markAll, isMarked);
}
- this._saveState(type, [objectId], isMarked);
- },
+ this.setParentAsMarked(checkbox, checkbox.checked);
+ this.saveState(type, [objectId], isMarked);
+ }
/**
* Saves the state for given item object ids.
- *
- * @param {string} type object type
- * @param {int[]} objectIds item object ids
- * @param {boolean} isMarked true if marked
*/
- _saveState: function (type, objectIds, isMarked) {
+ saveState(objectType, objectIds, isMarked) {
Ajax.api(this, {
- actionName: (isMarked ? 'mark' : 'unmark'),
+ actionName: isMarked ? "mark" : "unmark",
parameters: {
- pageClassNames: _options.pageClassNames,
- pageObjectID: _options.pageObjectId,
+ pageClassNames: this.pageClassNames,
+ pageObjectID: this.pageObjectId,
objectIDs: objectIds,
- objectType: type
- }
+ objectType,
+ },
});
- },
+ }
/**
* Executes an editor action.
- *
- * @param {object} event event object
*/
- _executeAction: function (event) {
- var listItem = event.currentTarget;
- var data = _itemData.get(listItem);
+ executeAction(event) {
+ const listItem = event.currentTarget;
+ const data = this.itemData.get(listItem);
if (data.url) {
window.location.href = data.url;
return;
}
- var triggerEvent = function () {
- var type = elData(listItem, 'type');
- EventHandler.fire('com.woltlab.wcf.clipboard', type, {
- data: data,
- listItem: listItem,
- responseData: null
+ function triggerEvent() {
+ const type = listItem.dataset.type;
+ EventHandler.fire("com.woltlab.wcf.clipboard", type, {
+ data,
+ listItem,
+ responseData: null,
});
- };
- //noinspection JSUnresolvedVariable
- var confirmMessage = (typeof data.internalData.confirmMessage === 'string') ? data.internalData.confirmMessage : '';
- var fireEvent = true;
- if (typeof data.parameters === 'object' && data.parameters.actionName && data.parameters.className) {
- if (data.parameters.actionName === 'unmarkAll' || Array.isArray(data.parameters.objectIDs)) {
- if (confirmMessage.length) {
- //noinspection JSUnresolvedVariable
- var template = (typeof data.internalData.template === 'string') ? data.internalData.template : '';
+ }
+ const message = typeof data.internalData.confirmMessage === "string" ? data.internalData.confirmMessage : "";
+ let fireEvent = true;
+ if (Core.isPlainObject(data.parameters) && data.parameters.actionName && data.parameters.className) {
+ if (data.parameters.actionName === "unmarkAll" || Array.isArray(data.parameters.objectIDs)) {
+ if (message.length) {
+ const template = typeof data.internalData.template === "string" ? data.internalData.template : "";
UiConfirmation.show({
- confirm: (function () {
- var formData = {};
+ confirm: () => {
+ const formData = {};
if (template.length) {
- var items = elBySelAll('input, select, textarea', UiConfirmation.getContentElement());
- for (var i = 0, length = items.length; i < length; i++) {
- var item = items[i];
- var name = elAttr(item, 'name');
+ UiConfirmation.getContentElement()
+ .querySelectorAll("input, select, textarea")
+ .forEach((item) => {
+ const name = item.name;
switch (item.nodeName) {
- case 'INPUT':
+ case "INPUT":
if ((item.type !== "checkbox" && item.type !== "radio") || item.checked) {
- formData[name] = elAttr(item, 'value');
+ formData[name] = item.value;
}
break;
- case 'SELECT':
+ case "SELECT":
formData[name] = item.value;
break;
- case 'TEXTAREA':
+ case "TEXTAREA":
formData[name] = item.value.trim();
break;
}
- }
+ });
}
- //noinspection JSUnresolvedFunction
this._executeProxyAction(listItem, data, formData);
- }).bind(this),
- message: confirmMessage,
- template: template
+ },
+ message,
+ template,
});
}
else {
}
}
}
- else if (confirmMessage.length) {
+ else if (message.length) {
fireEvent = false;
UiConfirmation.show({
confirm: triggerEvent,
- message: confirmMessage
+ message,
});
}
if (fireEvent) {
triggerEvent();
}
- },
+ }
/**
* Forwards clipboard actions to an individual handler.
- *
- * @param {Element} listItem dropdown item element
- * @param {Object} data action data
- * @param {Object?} formData form data
*/
- _executeProxyAction: function (listItem, data, formData) {
- formData = formData || {};
- var objectIds = (data.parameters.actionName !== 'unmarkAll') ? data.parameters.objectIDs : [];
- var parameters = { data: formData };
- //noinspection JSUnresolvedVariable
- if (typeof data.internalData.parameters === 'object') {
- //noinspection JSUnresolvedVariable
- for (var key in data.internalData.parameters) {
- //noinspection JSUnresolvedVariable
- if (data.internalData.parameters.hasOwnProperty(key)) {
- //noinspection JSUnresolvedVariable
- parameters[key] = data.internalData.parameters[key];
- }
- }
+ _executeProxyAction(listItem, data, formData = {}) {
+ const objectIds = data.parameters.actionName !== "unmarkAll" ? data.parameters.objectIDs : [];
+ const parameters = { data: formData };
+ if (Core.isPlainObject(data.internalData.parameters)) {
+ Object.entries(data.internalData.parameters).forEach(([key, value]) => {
+ parameters[key] = value;
+ });
}
Ajax.api(this, {
actionName: data.parameters.actionName,
className: data.parameters.className,
objectIDs: objectIds,
- parameters: parameters
- }, (function (responseData) {
- if (data.actionName !== 'unmarkAll') {
- var type = elData(listItem, 'type');
- EventHandler.fire('com.woltlab.wcf.clipboard', type, {
- data: data,
- listItem: listItem,
- responseData: responseData
+ parameters,
+ }, (responseData) => {
+ if (data.actionName !== "unmarkAll") {
+ const type = listItem.dataset.type;
+ EventHandler.fire("com.woltlab.wcf.clipboard", type, {
+ data,
+ listItem,
+ responseData,
});
- if (_reloadPageOnSuccess.has(type) && _reloadPageOnSuccess.get(type).indexOf(responseData.actionName) !== -1) {
+ const reloadPageOnSuccess = this.reloadPageOnSuccess.get(type);
+ if (reloadPageOnSuccess && reloadPageOnSuccess.includes(responseData.actionName)) {
window.location.reload();
return;
}
}
- this._loadMarkedItems();
- }).bind(this));
- },
+ this.loadMarkedItems();
+ });
+ }
/**
* Unmarks all clipboard items for an object type.
- *
- * @param {object} event event object
*/
- _unmarkAll: function (event) {
- var type = elData(event.currentTarget, 'type');
+ unmarkAll(event) {
+ const listItem = event.currentTarget;
Ajax.api(this, {
- actionName: 'unmarkAll',
+ actionName: "unmarkAll",
parameters: {
- objectType: type
- }
+ objectType: listItem.dataset.type,
+ },
});
- },
+ }
/**
* Sets up ajax request object.
- *
- * @return {object} request options
*/
- _ajaxSetup: function () {
+ _ajaxSetup() {
return {
data: {
- className: 'wcf\\data\\clipboard\\item\\ClipboardItemAction'
- }
+ className: "wcf\\data\\clipboard\\item\\ClipboardItemAction",
+ },
};
- },
+ }
/**
* Handles successful AJAX requests.
- *
- * @param {object} data response data
*/
- _ajaxSuccess: function (data) {
- if (data.actionName === 'unmarkAll') {
- _containers.forEach((function (containerData) {
- if (elData(containerData.element, 'type') === data.returnValues.objectType) {
- var clipboardObjects = elByClass('jsMarked', containerData.element);
- while (clipboardObjects.length) {
- clipboardObjects[0].classList.remove('jsMarked');
- }
- if (containerData.markAll !== null) {
- containerData.markAll.checked = false;
- if (elAttr(containerData.markAll.parentNode, 'role') === 'checkbox') {
- elAttr(containerData.markAll.parentNode, 'aria-checked', false);
- }
- }
- for (var i = 0, length = containerData.checkboxes.length; i < length; i++) {
- containerData.checkboxes[i].checked = false;
- if (elAttr(containerData.checkboxes[i].parentNode, 'role') === 'checkbox') {
- elAttr(containerData.checkboxes[i].parentNode, 'aria-checked', false);
- }
- }
- UiPageAction.remove('wcfClipboard-' + data.returnValues.objectType);
+ _ajaxSuccess(data) {
+ if (data.actionName === "unmarkAll") {
+ const objectType = data.returnValues.objectType;
+ this.containers.forEach((containerData) => {
+ if (containerData.element.dataset.type !== objectType) {
+ return;
+ }
+ containerData.element.querySelectorAll(".jsMarked").forEach((element) => element.classList.remove("jsMarked"));
+ if (containerData.markAll !== null) {
+ containerData.markAll.checked = false;
+ this.setParentAsMarked(containerData.markAll, false);
}
- }).bind(this));
+ Array.from(containerData.checkboxes).forEach((checkbox) => {
+ checkbox.checked = false;
+ this.setParentAsMarked(checkbox, false);
+ });
+ UiPageAction.remove(`wcfClipboard-${objectType}`);
+ });
return;
}
- _itemData = new ObjectMap();
- _reloadPageOnSuccess = new Dictionary();
+ this.itemData = new WeakMap();
+ this.reloadPageOnSuccess.clear();
// rebuild markings
- _containers.forEach((function (containerData) {
- var typeName = elData(containerData.element, 'type');
- //noinspection JSUnresolvedVariable
- var objectIds = (data.returnValues.markedItems && data.returnValues.markedItems.hasOwnProperty(typeName)) ? data.returnValues.markedItems[typeName] : [];
- this._rebuildMarkings(containerData, objectIds);
- }).bind(this));
- var keepEditors = [], typeName;
- if (data.returnValues && data.returnValues.items) {
- for (typeName in data.returnValues.items) {
- if (data.returnValues.items.hasOwnProperty(typeName)) {
- keepEditors.push(typeName);
- }
- }
- }
+ const markings = Core.isPlainObject(data.returnValues.markedItems) ? data.returnValues.markedItems : {};
+ this.containers.forEach((containerData) => {
+ const typeName = containerData.element.dataset.type;
+ const objectIds = Array.isArray(markings[typeName]) ? markings[typeName] : [];
+ this.rebuildMarkings(containerData, objectIds);
+ });
+ const keepEditors = Object.keys(data.returnValues.items || {});
// clear editors
- _editors.forEach(function (editor, typeName) {
- if (keepEditors.indexOf(typeName) === -1) {
- UiPageAction.remove('wcfClipboard-' + typeName);
- _editorDropdowns.get(typeName).innerHTML = '';
+ this.editors.forEach((editor, typeName) => {
+ if (keepEditors.includes(typeName)) {
+ UiPageAction.remove(`wcfClipboard-${typeName}`);
+ this.editorDropdowns.get(typeName).innerHTML = "";
}
});
// no items
- if (!data.returnValues || !data.returnValues.items) {
+ if (!data.returnValues.items) {
return;
}
// rebuild editors
- var actionName, created, dropdown, editor, typeData;
- var divider, item, itemData, itemIndex, label, unmarkAll;
- for (typeName in data.returnValues.items) {
- if (!data.returnValues.items.hasOwnProperty(typeName)) {
- continue;
- }
- typeData = data.returnValues.items[typeName];
- //noinspection JSUnresolvedVariable
- _reloadPageOnSuccess.set(typeName, typeData.reloadPageOnSuccess);
- created = false;
- editor = _editors.get(typeName);
- dropdown = _editorDropdowns.get(typeName);
+ Object.entries(data.returnValues.items).forEach(([typeName, typeData]) => {
+ this.reloadPageOnSuccess.set(typeName, typeData.reloadPageOnSuccess);
+ let created = false;
+ let editor = this.editors.get(typeName);
+ let dropdown = this.editorDropdowns.get(typeName);
if (editor === undefined) {
created = true;
- editor = elCreate('a');
- editor.className = 'dropdownToggle';
+ editor = document.createElement("a");
+ editor.className = "dropdownToggle";
editor.textContent = typeData.label;
- _editors.set(typeName, editor);
- dropdown = elCreate('ol');
- dropdown.className = 'dropdownMenu';
- _editorDropdowns.set(typeName, dropdown);
+ this.editors.set(typeName, editor);
+ dropdown = document.createElement("ol");
+ dropdown.className = "dropdownMenu";
+ this.editorDropdowns.set(typeName, dropdown);
}
else {
editor.textContent = typeData.label;
- dropdown.innerHTML = '';
+ dropdown.innerHTML = "";
}
// create editor items
- for (itemIndex in typeData.items) {
- if (!typeData.items.hasOwnProperty(itemIndex)) {
- continue;
- }
- itemData = typeData.items[itemIndex];
- item = elCreate('li');
- label = elCreate('span');
+ Object.values(typeData.items).forEach((itemData) => {
+ const item = document.createElement("li");
+ const label = document.createElement("span");
label.textContent = itemData.label;
item.appendChild(label);
dropdown.appendChild(item);
- elData(item, 'type', typeName);
- item.addEventListener('click', _callbackItem);
- _itemData.set(item, itemData);
- }
- divider = elCreate('li');
- divider.classList.add('dropdownDivider');
+ item.dataset.type = typeName;
+ item.addEventListener("click", (ev) => this.executeAction(ev));
+ this.itemData.set(item, itemData);
+ });
+ const divider = document.createElement("li");
+ divider.classList.add("dropdownDivider");
dropdown.appendChild(divider);
// add 'unmark all'
- unmarkAll = elCreate('li');
- elData(unmarkAll, 'type', typeName);
- label = elCreate('span');
- label.textContent = Language.get('wcf.clipboard.item.unmarkAll');
+ const unmarkAll = document.createElement("li");
+ unmarkAll.dataset.type = typeName;
+ const label = document.createElement("span");
+ label.textContent = Language.get("wcf.clipboard.item.unmarkAll");
unmarkAll.appendChild(label);
- unmarkAll.addEventListener('click', _callbackUnmarkAll);
+ unmarkAll.addEventListener("click", (ev) => this.unmarkAll(ev));
dropdown.appendChild(unmarkAll);
if (keepEditors.indexOf(typeName) !== -1) {
- actionName = 'wcfClipboard-' + typeName;
+ const actionName = `wcfClipboard-${typeName}`;
if (UiPageAction.has(actionName)) {
UiPageAction.show(actionName);
}
}
}
if (created) {
- editor.parentNode.classList.add('dropdown');
- editor.parentNode.appendChild(dropdown);
- UiSimpleDropdown.init(editor);
+ const parent = editor.parentElement;
+ parent.classList.add("dropdown");
+ parent.appendChild(dropdown);
+ Simple_1.default.init(editor);
}
- }
- },
+ });
+ }
/**
* Rebuilds the mark state for each item.
- *
- * @param {Object} data container data
- * @param {int[]} objectIds item object ids
*/
- _rebuildMarkings: function (data, objectIds) {
- var markAll = true;
- for (var i = 0, length = data.checkboxes.length; i < length; i++) {
- var checkbox = data.checkboxes[i];
- var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
- var isMarked = (objectIds.indexOf(~~elData(checkbox, 'object-id')) !== -1);
- if (!isMarked)
+ rebuildMarkings(data, objectIds) {
+ let markAll = true;
+ Array.from(data.checkboxes).forEach((checkbox) => {
+ const clipboardObject = checkbox.closest(".jsClipboardObject");
+ const isMarked = objectIds.includes(~~checkbox.dataset.objectId);
+ if (!isMarked) {
markAll = false;
+ }
checkbox.checked = isMarked;
- clipboardObject.classList[(isMarked ? 'add' : 'remove')]('jsMarked');
- if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
- elAttr(checkbox.parentNode, 'aria-checked', isMarked);
+ if (isMarked) {
+ clipboardObject.classList.add("jsMarked");
}
- }
+ else {
+ clipboardObject.classList.remove("jsMarked");
+ }
+ this.setParentAsMarked(checkbox, isMarked);
+ });
if (data.markAll !== null) {
data.markAll.checked = markAll;
- if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
- elAttr(data.markAll.parentNode, 'aria-checked', markAll);
- }
- var parent = data.markAll;
- while (parent = parent.parentNode) {
- if (parent instanceof Element && parent.classList.contains('columnMark')) {
- parent = parent.parentNode;
- break;
- }
- }
+ this.setParentAsMarked(data.markAll, markAll);
+ const parent = data.markAll.closest(".columnMark");
if (parent) {
- parent.classList[(markAll ? 'add' : 'remove')]('jsMarked');
+ if (markAll) {
+ parent.classList.add("jsMarked");
+ }
+ else {
+ parent.classList.remove("jsMarked");
+ }
}
}
- },
+ }
+ setParentAsMarked(element, isMarked) {
+ const parent = element.parentElement;
+ if (parent.getAttribute("role") === "checkbox") {
+ parent.setAttribute("aria-checked", isMarked ? "true" : "false");
+ }
+ }
/**
* Hides the clipboard editor for the given object type.
- *
- * @param {string} objectType
*/
- hideEditor: function (objectType) {
- UiPageAction.remove('wcfClipboard-' + objectType);
+ hideEditor(objectType) {
+ UiPageAction.remove("wcfClipboard-" + objectType);
UiScreen.pageOverlayOpen();
- },
+ }
/**
* Shows the clipboard editor.
*/
- showEditor: function () {
- this._loadMarkedItems();
+ showEditor() {
+ this.loadMarkedItems();
UiScreen.pageOverlayClose();
- },
+ }
/**
* Unmarks the objects with given clipboard object type and ids.
- *
- * @param {string} objectType
- * @param {int[]} objectIds
*/
- unmark: function (objectType, objectIds) {
- this._saveState(objectType, objectIds, false);
+ unmark(objectType, objectIds) {
+ this.saveState(objectType, objectIds, false);
}
- };
+ }
+ let controllerClipboard;
+ function getControllerClipboard() {
+ if (!controllerClipboard) {
+ controllerClipboard = new ControllerClipboard();
+ }
+ return controllerClipboard;
+ }
+ /**
+ * Initializes the clipboard API handler.
+ */
+ function setup(options) {
+ getControllerClipboard().setup(options);
+ }
+ exports.setup = setup;
+ /**
+ * Reloads the clipboard data.
+ */
+ function reload() {
+ getControllerClipboard().reload();
+ }
+ exports.reload = reload;
+ /**
+ * Hides the clipboard editor for the given object type.
+ */
+ function hideEditor(objectType) {
+ getControllerClipboard().hideEditor(objectType);
+ }
+ exports.hideEditor = hideEditor;
+ /**
+ * Shows the clipboard editor.
+ */
+ function showEditor() {
+ getControllerClipboard().showEditor();
+ }
+ exports.showEditor = showEditor;
+ /**
+ * Unmarks the objects with given clipboard object type and ids.
+ */
+ function unmark(objectType, objectIds) {
+ getControllerClipboard().unmark(objectType, objectIds);
+ }
+ exports.unmark = unmark;
});
+++ /dev/null
-/**
- * Clipboard API Handler.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Controller/Clipboard
- */
-define(
- [
- 'Ajax', 'Core', 'Dictionary', 'EventHandler',
- 'Language', 'List', 'ObjectMap', 'Dom/ChangeListener',
- 'Dom/Traverse', 'Dom/Util', 'Ui/Confirmation', 'Ui/SimpleDropdown',
- 'WoltLabSuite/Core/Ui/Page/Action', 'Ui/Screen'
- ],
- function(
- Ajax, Core, Dictionary, EventHandler,
- Language, List, ObjectMap, DomChangeListener,
- DomTraverse, DomUtil, UiConfirmation, UiSimpleDropdown,
- UiPageAction, UiScreen
- )
-{
- "use strict";
-
- if (!COMPILER_TARGET_DEFAULT) {
- return {
- setup: function() {},
- reload: function() {},
- _initContainers: function() {},
- _loadMarkedItems: function() {},
- _markAll: function() {},
- _mark: function() {},
- _saveState: function() {},
- _executeAction: function() {},
- _executeProxyAction: function() {},
- _unmarkAll: function() {},
- _ajaxSetup: function() {},
- _ajaxSuccess: function() {},
- _rebuildMarkings: function() {},
- hideEditor: function() {},
- showEditor: function() {},
- unmark: function() {}
- };
- }
-
- var _containers = new Dictionary();
- var _editors = new Dictionary();
- var _editorDropdowns = new Dictionary();
- var _elements = elByClass('jsClipboardContainer');
- var _itemData = new ObjectMap();
- var _knownCheckboxes = new List();
- var _options = {};
- var _reloadPageOnSuccess = new Dictionary();
-
- var _callbackCheckbox = null;
- var _callbackItem = null;
- var _callbackUnmarkAll = null;
-
- var _specialCheckboxSelector = '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]';
-
- /**
- * Clipboard API
- *
- * @exports WoltLabSuite/Core/Controller/Clipboard
- */
- return {
- /**
- * Initializes the clipboard API handler.
- *
- * @param {Object} options initialization options
- */
- setup: function(options) {
- if (!options.pageClassName) {
- throw new Error("Expected a non-empty string for parameter 'pageClassName'.");
- }
-
- if (_callbackCheckbox === null) {
- _callbackCheckbox = this._mark.bind(this);
- _callbackItem = this._executeAction.bind(this);
- _callbackUnmarkAll = this._unmarkAll.bind(this);
-
- _options = Core.extend({
- hasMarkedItems: false,
- pageClassNames: [options.pageClassName],
- pageObjectId: 0
- }, options);
-
- delete _options.pageClassName;
- }
- else {
- if (options.pageObjectId) {
- throw new Error("Cannot load secondary clipboard with page object id set.");
- }
-
- _options.pageClassNames.push(options.pageClassName);
- }
-
- if (!Element.prototype.matches) {
- Element.prototype.matches = Element.prototype.msMatchesSelector;
- }
-
- this._initContainers();
-
- if (_options.hasMarkedItems && _elements.length) {
- this._loadMarkedItems();
- }
-
- DomChangeListener.add('WoltLabSuite/Core/Controller/Clipboard', this._initContainers.bind(this));
- },
-
- /**
- * Reloads the clipboard data.
- */
- reload: function() {
- if (_containers.size) {
- this._loadMarkedItems();
- }
- },
-
- /**
- * Initializes clipboard containers.
- */
- _initContainers: function() {
- for (var i = 0, length = _elements.length; i < length; i++) {
- var container = _elements[i];
- var containerId = DomUtil.identify(container);
- var containerData = _containers.get(containerId);
-
- if (containerData === undefined) {
- var markAll = elBySel('.jsClipboardMarkAll', container);
-
- if (markAll !== null) {
- if (markAll.matches(_specialCheckboxSelector)) {
- var label = markAll.closest('label');
- elAttr(label, 'role', 'checkbox');
- elAttr(label, 'tabindex', '0');
- elAttr(label, 'aria-checked', false);
- elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.markAll'));
-
- label.addEventListener('keyup', function (event) {
- if (event.keyCode === 13 || event.keyCode === 32) {
- checkbox.click();
- }
- });
- }
-
- elData(markAll, 'container-id', containerId);
- markAll.addEventListener('click', this._markAll.bind(this));
- }
-
- containerData = {
- checkboxes: elByClass('jsClipboardItem', container),
- element: container,
- markAll: markAll,
- markedObjectIds: new List()
- };
- _containers.set(containerId, containerData);
- }
-
- for (var j = 0, innerLength = containerData.checkboxes.length; j < innerLength; j++) {
- var checkbox = containerData.checkboxes[j];
-
- if (!_knownCheckboxes.has(checkbox)) {
- elData(checkbox, 'container-id', containerId);
-
- (function(checkbox) {
- if (checkbox.matches(_specialCheckboxSelector)) {
- var label = checkbox.closest('label');
- elAttr(label, 'role', 'checkbox');
- elAttr(label, 'tabindex', '0');
- elAttr(label, 'aria-checked', false);
- elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.mark'));
-
- label.addEventListener('keyup', function (event) {
- if (event.keyCode === 13 || event.keyCode === 32) {
- checkbox.click();
- }
- });
- }
-
- var link = checkbox.closest('a');
- if (link === null) {
- checkbox.addEventListener('click', _callbackCheckbox);
- }
- else {
- // Firefox will always trigger the link if the checkbox is
- // inside of one. Since 2000. Thanks Firefox.
- checkbox.addEventListener('click', function (event) {
- event.preventDefault();
-
- window.setTimeout(function () {
- checkbox.checked = !checkbox.checked;
-
- _callbackCheckbox(null, checkbox);
- }, 10);
- });
- }
- })(checkbox);
-
- _knownCheckboxes.add(checkbox);
- }
- }
- }
- },
-
- /**
- * Loads marked items from clipboard.
- */
- _loadMarkedItems: function() {
- Ajax.api(this, {
- actionName: 'getMarkedItems',
- parameters: {
- pageClassNames: _options.pageClassNames,
- pageObjectID: _options.pageObjectId
- }
- });
- },
-
- /**
- * Marks or unmarks all visible items at once.
- *
- * @param {object} event event object
- */
- _markAll: function(event) {
- var checkbox = event.currentTarget;
- var isMarked = (checkbox.nodeName !== 'INPUT' || checkbox.checked);
-
- if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
- elAttr(checkbox.parentNode, 'aria-checked', isMarked);
- }
-
- var objectIds = [];
-
- var containerId = elData(checkbox, 'container-id');
- var data = _containers.get(containerId);
- var type = elData(data.element, 'type');
-
- for (var i = 0, length = data.checkboxes.length; i < length; i++) {
- var item = data.checkboxes[i];
- var objectId = ~~elData(item, 'object-id');
-
- if (isMarked) {
- if (!item.checked) {
- item.checked = true;
-
- data.markedObjectIds.add(objectId);
- objectIds.push(objectId);
- }
- }
- else {
- if (item.checked) {
- item.checked = false;
-
- data.markedObjectIds['delete'](objectId);
- objectIds.push(objectId);
- }
- }
-
- if (elAttr(item.parentNode, 'role') === 'checkbox') {
- elAttr(item.parentNode, 'aria-checked', isMarked);
- }
-
- var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
- if (clipboardObject !== null) {
- clipboardObject.classList[(isMarked ? 'addClass' : 'removeClass')]('jsMarked');
- }
- }
-
- this._saveState(type, objectIds, isMarked);
- },
-
- /**
- * Marks or unmarks an individual item.
- *
- * @param {object} event event object
- * @param {Element=} checkbox checkbox element
- */
- _mark: function(event, checkbox) {
- checkbox = (event instanceof Event) ? event.currentTarget : checkbox;
- var objectId = ~~elData(checkbox, 'object-id');
- var isMarked = checkbox.checked;
- var containerId = elData(checkbox, 'container-id');
- var data = _containers.get(containerId);
- var type = elData(data.element, 'type');
-
- var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
- data.markedObjectIds[(isMarked ? 'add' : 'delete')](objectId);
- clipboardObject.classList[(isMarked) ? 'add' : 'remove']('jsMarked');
-
- if (data.markAll !== null) {
- var markedAll = true;
- for (var i = 0, length = data.checkboxes.length; i < length; i++) {
- if (!data.checkboxes[i].checked) {
- markedAll = false;
-
- break;
- }
- }
-
- data.markAll.checked = markedAll;
-
- if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
- elAttr(data.markAll.parentNode, 'aria-checked', isMarked);
- }
- }
-
- if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
- elAttr(checkbox.parentNode, 'aria-checked', checkbox.checked);
- }
-
- this._saveState(type, [ objectId ], isMarked);
- },
-
- /**
- * Saves the state for given item object ids.
- *
- * @param {string} type object type
- * @param {int[]} objectIds item object ids
- * @param {boolean} isMarked true if marked
- */
- _saveState: function(type, objectIds, isMarked) {
- Ajax.api(this, {
- actionName: (isMarked ? 'mark' : 'unmark'),
- parameters: {
- pageClassNames: _options.pageClassNames,
- pageObjectID: _options.pageObjectId,
- objectIDs: objectIds,
- objectType: type
- }
- });
- },
-
- /**
- * Executes an editor action.
- *
- * @param {object} event event object
- */
- _executeAction: function(event) {
- var listItem = event.currentTarget;
- var data = _itemData.get(listItem);
-
- if (data.url) {
- window.location.href = data.url;
- return;
- }
-
- var triggerEvent = function() {
- var type = elData(listItem, 'type');
-
- EventHandler.fire('com.woltlab.wcf.clipboard', type, {
- data: data,
- listItem: listItem,
- responseData: null
- });
- };
-
- //noinspection JSUnresolvedVariable
- var confirmMessage = (typeof data.internalData.confirmMessage === 'string') ? data.internalData.confirmMessage : '';
- var fireEvent = true;
-
- if (typeof data.parameters === 'object' && data.parameters.actionName && data.parameters.className) {
- if (data.parameters.actionName === 'unmarkAll' || Array.isArray(data.parameters.objectIDs)) {
- if (confirmMessage.length) {
- //noinspection JSUnresolvedVariable
- var template = (typeof data.internalData.template === 'string') ? data.internalData.template : '';
-
- UiConfirmation.show({
- confirm: (function() {
- var formData = {};
-
- if (template.length) {
- var items = elBySelAll('input, select, textarea', UiConfirmation.getContentElement());
- for (var i = 0, length = items.length; i < length; i++) {
- var item = items[i];
- var name = elAttr(item, 'name');
-
- switch (item.nodeName) {
- case 'INPUT':
- if ((item.type !== "checkbox" && item.type !== "radio") || item.checked) {
- formData[name] = elAttr(item, 'value');
- }
- break;
-
- case 'SELECT':
- formData[name] = item.value;
- break;
-
- case 'TEXTAREA':
- formData[name] = item.value.trim();
- break;
- }
- }
- }
-
- //noinspection JSUnresolvedFunction
- this._executeProxyAction(listItem, data, formData);
- }).bind(this),
- message: confirmMessage,
- template: template
- });
- }
- else {
- this._executeProxyAction(listItem, data);
- }
- }
- }
- else if (confirmMessage.length) {
- fireEvent = false;
-
- UiConfirmation.show({
- confirm: triggerEvent,
- message: confirmMessage
- });
- }
-
- if (fireEvent) {
- triggerEvent();
- }
- },
-
- /**
- * Forwards clipboard actions to an individual handler.
- *
- * @param {Element} listItem dropdown item element
- * @param {Object} data action data
- * @param {Object?} formData form data
- */
- _executeProxyAction: function(listItem, data, formData) {
- formData = formData || {};
-
- var objectIds = (data.parameters.actionName !== 'unmarkAll') ? data.parameters.objectIDs : [];
- var parameters = { data: formData };
-
- //noinspection JSUnresolvedVariable
- if (typeof data.internalData.parameters === 'object') {
- //noinspection JSUnresolvedVariable
- for (var key in data.internalData.parameters) {
- //noinspection JSUnresolvedVariable
- if (data.internalData.parameters.hasOwnProperty(key)) {
- //noinspection JSUnresolvedVariable
- parameters[key] = data.internalData.parameters[key];
- }
- }
- }
-
- Ajax.api(this, {
- actionName: data.parameters.actionName,
- className: data.parameters.className,
- objectIDs: objectIds,
- parameters: parameters
- }, (function(responseData) {
- if (data.actionName !== 'unmarkAll') {
- var type = elData(listItem, 'type');
-
- EventHandler.fire('com.woltlab.wcf.clipboard', type, {
- data: data,
- listItem: listItem,
- responseData: responseData
- });
-
- if (_reloadPageOnSuccess.has(type) && _reloadPageOnSuccess.get(type).indexOf(responseData.actionName) !== -1) {
- window.location.reload();
- return;
- }
- }
-
- this._loadMarkedItems();
- }).bind(this));
- },
-
- /**
- * Unmarks all clipboard items for an object type.
- *
- * @param {object} event event object
- */
- _unmarkAll: function(event) {
- var type = elData(event.currentTarget, 'type');
-
- Ajax.api(this, {
- actionName: 'unmarkAll',
- parameters: {
- objectType: type
- }
- });
- },
-
- /**
- * Sets up ajax request object.
- *
- * @return {object} request options
- */
- _ajaxSetup: function() {
- return {
- data: {
- className: 'wcf\\data\\clipboard\\item\\ClipboardItemAction'
- }
- };
- },
-
- /**
- * Handles successful AJAX requests.
- *
- * @param {object} data response data
- */
- _ajaxSuccess: function(data) {
- if (data.actionName === 'unmarkAll') {
- _containers.forEach((function(containerData) {
- if (elData(containerData.element, 'type') === data.returnValues.objectType) {
- var clipboardObjects = elByClass('jsMarked', containerData.element);
- while (clipboardObjects.length) {
- clipboardObjects[0].classList.remove('jsMarked');
- }
-
- if (containerData.markAll !== null) {
- containerData.markAll.checked = false;
-
- if (elAttr(containerData.markAll.parentNode, 'role') === 'checkbox') {
- elAttr(containerData.markAll.parentNode, 'aria-checked', false);
- }
- }
- for (var i = 0, length = containerData.checkboxes.length; i < length; i++) {
- containerData.checkboxes[i].checked = false;
-
- if (elAttr(containerData.checkboxes[i].parentNode, 'role') === 'checkbox') {
- elAttr(containerData.checkboxes[i].parentNode, 'aria-checked', false);
- }
- }
-
- UiPageAction.remove('wcfClipboard-' + data.returnValues.objectType);
- }
- }).bind(this));
-
- return;
- }
-
- _itemData = new ObjectMap();
- _reloadPageOnSuccess = new Dictionary();
-
- // rebuild markings
- _containers.forEach((function(containerData) {
- var typeName = elData(containerData.element, 'type');
-
- //noinspection JSUnresolvedVariable
- var objectIds = (data.returnValues.markedItems && data.returnValues.markedItems.hasOwnProperty(typeName)) ? data.returnValues.markedItems[typeName] : [];
- this._rebuildMarkings(containerData, objectIds);
- }).bind(this));
-
- var keepEditors = [], typeName;
- if (data.returnValues && data.returnValues.items) {
- for (typeName in data.returnValues.items) {
- if (data.returnValues.items.hasOwnProperty(typeName)) {
- keepEditors.push(typeName);
- }
- }
- }
-
- // clear editors
- _editors.forEach(function(editor, typeName) {
- if (keepEditors.indexOf(typeName) === -1) {
- UiPageAction.remove('wcfClipboard-' + typeName);
-
- _editorDropdowns.get(typeName).innerHTML = '';
- }
- });
-
- // no items
- if (!data.returnValues || !data.returnValues.items) {
- return;
- }
-
- // rebuild editors
- var actionName, created, dropdown, editor, typeData;
- var divider, item, itemData, itemIndex, label, unmarkAll;
- for (typeName in data.returnValues.items) {
- if (!data.returnValues.items.hasOwnProperty(typeName)) {
- continue;
- }
-
- typeData = data.returnValues.items[typeName];
- //noinspection JSUnresolvedVariable
- _reloadPageOnSuccess.set(typeName, typeData.reloadPageOnSuccess);
- created = false;
-
- editor = _editors.get(typeName);
- dropdown = _editorDropdowns.get(typeName);
- if (editor === undefined) {
- created = true;
-
- editor = elCreate('a');
- editor.className = 'dropdownToggle';
- editor.textContent = typeData.label;
-
- _editors.set(typeName, editor);
-
- dropdown = elCreate('ol');
- dropdown.className = 'dropdownMenu';
-
- _editorDropdowns.set(typeName, dropdown);
- }
- else {
- editor.textContent = typeData.label;
- dropdown.innerHTML = '';
- }
-
- // create editor items
- for (itemIndex in typeData.items) {
- if (!typeData.items.hasOwnProperty(itemIndex)) {
- continue;
- }
-
- itemData = typeData.items[itemIndex];
-
- item = elCreate('li');
- label = elCreate('span');
- label.textContent = itemData.label;
- item.appendChild(label);
- dropdown.appendChild(item);
-
- elData(item, 'type', typeName);
- item.addEventListener('click', _callbackItem);
-
- _itemData.set(item, itemData);
- }
-
- divider = elCreate('li');
- divider.classList.add('dropdownDivider');
- dropdown.appendChild(divider);
-
- // add 'unmark all'
- unmarkAll = elCreate('li');
- elData(unmarkAll, 'type', typeName);
- label = elCreate('span');
- label.textContent = Language.get('wcf.clipboard.item.unmarkAll');
- unmarkAll.appendChild(label);
- unmarkAll.addEventListener('click', _callbackUnmarkAll);
- dropdown.appendChild(unmarkAll);
-
- if (keepEditors.indexOf(typeName) !== -1) {
- actionName = 'wcfClipboard-' + typeName;
-
- if (UiPageAction.has(actionName)) {
- UiPageAction.show(actionName);
- }
- else {
- UiPageAction.add(actionName, editor);
- }
- }
-
- if (created) {
- editor.parentNode.classList.add('dropdown');
- editor.parentNode.appendChild(dropdown);
- UiSimpleDropdown.init(editor);
- }
- }
- },
-
- /**
- * Rebuilds the mark state for each item.
- *
- * @param {Object} data container data
- * @param {int[]} objectIds item object ids
- */
- _rebuildMarkings: function(data, objectIds) {
- var markAll = true;
-
- for (var i = 0, length = data.checkboxes.length; i < length; i++) {
- var checkbox = data.checkboxes[i];
- var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
-
- var isMarked = (objectIds.indexOf(~~elData(checkbox, 'object-id')) !== -1);
- if (!isMarked) markAll = false;
-
- checkbox.checked = isMarked;
- clipboardObject.classList[(isMarked ? 'add' : 'remove')]('jsMarked');
-
- if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
- elAttr(checkbox.parentNode, 'aria-checked', isMarked);
- }
- }
-
- if (data.markAll !== null) {
- data.markAll.checked = markAll;
-
- if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
- elAttr(data.markAll.parentNode, 'aria-checked', markAll);
- }
-
- var parent = data.markAll;
- while (parent = parent.parentNode) {
- if (parent instanceof Element && parent.classList.contains('columnMark')) {
- parent = parent.parentNode;
- break;
- }
- }
-
- if (parent) {
- parent.classList[(markAll ? 'add' : 'remove')]('jsMarked');
- }
- }
- },
-
- /**
- * Hides the clipboard editor for the given object type.
- *
- * @param {string} objectType
- */
- hideEditor: function(objectType) {
- UiPageAction.remove('wcfClipboard-' + objectType);
-
- UiScreen.pageOverlayOpen();
- },
-
- /**
- * Shows the clipboard editor.
- */
- showEditor: function() {
- this._loadMarkedItems();
-
- UiScreen.pageOverlayClose();
- },
-
- /**
- * Unmarks the objects with given clipboard object type and ids.
- *
- * @param {string} objectType
- * @param {int[]} objectIds
- */
- unmark: function(objectType, objectIds) {
- this._saveState(objectType, objectIds, false);
- }
- };
-});
--- /dev/null
+/**
+ * Clipboard API Handler.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Clipboard
+ */
+
+import * as Ajax from "../Ajax";
+import { AjaxCallbackSetup } from "../Ajax/Data";
+import * as Core from "../Core";
+import DomChangeListener from "../Dom/Change/Listener";
+import DomUtil from "../Dom/Util";
+import * as EventHandler from "../Event/Handler";
+import * as Language from "../Language";
+import * as UiConfirmation from "../Ui/Confirmation";
+import UiDropdownSimple from "../Ui/Dropdown/Simple";
+import * as UiPageAction from "../Ui/Page/Action";
+import * as UiScreen from "../Ui/Screen";
+
+interface ClipboardOptions {
+ hasMarkedItems: boolean;
+ pageClassName: string;
+ pageObjectId: number;
+}
+
+interface ContainerData {
+ checkboxes: HTMLCollectionOf<HTMLInputElement>;
+ element: HTMLElement;
+ markAll: HTMLInputElement | null;
+ markedObjectIds: Set<number>;
+}
+
+interface ItemData {
+ items: { [key: string]: ClipboardActionData };
+ label: string;
+ reloadPageOnSuccess: string[];
+}
+
+interface ClipboardActionData {
+ actionName: string;
+ internalData: ArbitraryObject;
+ label: string;
+ parameters: {
+ actionName?: string;
+ className?: string;
+ objectIDs: number[];
+ template: string;
+ };
+ url: string;
+}
+
+interface AjaxResponseMarkedItems {
+ [key: string]: number[];
+}
+
+interface AjaxResponse {
+ actionName: string;
+ returnValues: {
+ action: string;
+ items?: {
+ // They key is the `typeName`
+ [key: string]: ItemData;
+ };
+ markedItems?: AjaxResponseMarkedItems;
+ objectType: string;
+ };
+}
+
+const _specialCheckboxSelector =
+ '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]';
+
+class ControllerClipboard {
+ private readonly containers = new Map<string, ContainerData>();
+ private readonly editors = new Map<string, HTMLAnchorElement>();
+ private readonly editorDropdowns = new Map<string, HTMLOListElement>();
+ private itemData = new WeakMap<HTMLLIElement, ClipboardActionData>();
+ private readonly knownCheckboxes = new WeakSet<HTMLInputElement>();
+ private readonly pageClassNames: string[] = [];
+ private pageObjectId = 0;
+ private readonly reloadPageOnSuccess = new Map<string, string[]>();
+
+ /**
+ * Initializes the clipboard API handler.
+ */
+ setup(options: ClipboardOptions) {
+ if (!options.pageClassName) {
+ throw new Error("Expected a non-empty string for parameter 'pageClassName'.");
+ }
+
+ let hasMarkedItems = false;
+ if (this.pageClassNames.length === 0) {
+ hasMarkedItems = options.hasMarkedItems;
+ this.pageObjectId = options.pageObjectId;
+ }
+
+ this.pageClassNames.push(options.pageClassName);
+
+ this.initContainers();
+
+ if (hasMarkedItems && this.containers.size) {
+ this.loadMarkedItems();
+ }
+
+ DomChangeListener.add("WoltLabSuite/Core/Controller/Clipboard", () => this.initContainers());
+ }
+
+ /**
+ * Reloads the clipboard data.
+ */
+ reload(): void {
+ if (this.containers.size) {
+ this.loadMarkedItems();
+ }
+ }
+
+ /**
+ * Initializes clipboard containers.
+ */
+ private initContainers(): void {
+ document.querySelectorAll(".jsClipboardContainer").forEach((container: HTMLElement) => {
+ const containerId = DomUtil.identify(container);
+
+ let containerData = this.containers.get(containerId);
+ if (containerData === undefined) {
+ const markAll = container.querySelector(".jsClipboardMarkAll") as HTMLInputElement;
+
+ if (markAll !== null) {
+ if (markAll.matches(_specialCheckboxSelector)) {
+ const label = markAll.closest("label") as HTMLLabelElement;
+ label.setAttribute("role", "checkbox");
+ label.tabIndex = 0;
+ label.setAttribute("aria-checked", "false");
+ label.setAttribute("aria-label", Language.get("wcf.clipboard.item.markAll"));
+
+ label.addEventListener("keyup", (event) => {
+ if (event.key === "Enter" || event.key === "Space") {
+ markAll.click();
+ }
+ });
+ }
+
+ markAll.dataset.containerId = containerId;
+ markAll.addEventListener("click", (ev) => this.markAll(ev));
+ }
+
+ containerData = {
+ checkboxes: container.getElementsByClassName("jsClipboardItem") as HTMLCollectionOf<HTMLInputElement>,
+ element: container,
+ markAll: markAll,
+ markedObjectIds: new Set<number>(),
+ };
+ this.containers.set(containerId, containerData);
+ }
+
+ Array.from(containerData.checkboxes).forEach((checkbox) => {
+ if (this.knownCheckboxes.has(checkbox)) {
+ return;
+ }
+
+ checkbox.dataset.containerId = containerId;
+
+ if (checkbox.matches(_specialCheckboxSelector)) {
+ const label = checkbox.closest("label") as HTMLLabelElement;
+ label.setAttribute("role", "checkbox");
+ label.tabIndex = 0;
+ label.setAttribute("aria-checked", "false");
+ label.setAttribute("aria-label", Language.get("wcf.clipboard.item.mark"));
+
+ label.addEventListener("keyup", (event) => {
+ if (event.key === "Enter" || event.key === "Space") {
+ checkbox.click();
+ }
+ });
+ }
+
+ const link = checkbox.closest("a");
+ if (link === null) {
+ checkbox.addEventListener("click", (ev) => this.mark(ev));
+ } else {
+ // Firefox will always trigger the link if the checkbox is
+ // inside of one. Since 2000. Thanks Firefox.
+ checkbox.addEventListener("click", (event) => {
+ event.preventDefault();
+
+ window.setTimeout(() => {
+ checkbox.checked = !checkbox.checked;
+
+ this.mark(checkbox);
+ }, 10);
+ });
+ }
+
+ this.knownCheckboxes.add(checkbox);
+ });
+ });
+ }
+
+ /**
+ * Loads marked items from clipboard.
+ */
+ private loadMarkedItems(): void {
+ Ajax.api(this, {
+ actionName: "getMarkedItems",
+ parameters: {
+ pageClassNames: this.pageClassNames,
+ pageObjectID: this.pageObjectId,
+ },
+ });
+ }
+
+ /**
+ * Marks or unmarks all visible items at once.
+ */
+ private markAll(event: MouseEvent): void {
+ const checkbox = event.currentTarget as HTMLInputElement;
+ const isMarked = checkbox.nodeName !== "INPUT" || checkbox.checked;
+
+ this.setParentAsMarked(checkbox, isMarked);
+
+ const objectIds: number[] = [];
+
+ const containerId = checkbox.dataset.containerId!;
+ const data = this.containers.get(containerId)!;
+ const type = data.element.dataset.type!;
+
+ Array.from(data.checkboxes).forEach((item) => {
+ const objectId = ~~item.dataset.objectId!;
+
+ if (isMarked) {
+ if (!item.checked) {
+ item.checked = true;
+
+ data.markedObjectIds.add(objectId);
+ objectIds.push(objectId);
+ }
+ } else {
+ if (item.checked) {
+ item.checked = false;
+
+ data.markedObjectIds["delete"](objectId);
+ objectIds.push(objectId);
+ }
+ }
+
+ this.setParentAsMarked(item, isMarked);
+
+ const clipboardObject = checkbox.closest(".jsClipboardObject");
+ if (clipboardObject !== null) {
+ if (isMarked) {
+ clipboardObject.classList.add("jsMarked");
+ } else {
+ clipboardObject.classList.remove("jsMarked");
+ }
+ }
+ });
+
+ this.saveState(type, objectIds, isMarked);
+ }
+
+ /**
+ * Marks or unmarks an individual item.
+ *
+ */
+ private mark(event: MouseEvent | HTMLInputElement): void {
+ const checkbox = event instanceof Event ? (event.currentTarget as HTMLInputElement) : event;
+
+ const objectId = ~~checkbox.dataset.objectId!;
+ const isMarked = checkbox.checked;
+ const containerId = checkbox.dataset.containerId!;
+ const data = this.containers.get(containerId)!;
+ const type = data.element.dataset.type!;
+
+ const clipboardObject = checkbox.closest(".jsClipboardObject") as HTMLElement;
+ if (isMarked) {
+ data.markedObjectIds.add(objectId);
+ clipboardObject.classList.add("jsMarked");
+ } else {
+ data.markedObjectIds.delete(objectId);
+ clipboardObject.classList.remove("jsMarked");
+ }
+
+ if (data.markAll !== null) {
+ data.markAll.checked = Array.from(data.checkboxes).some((item) => !item.checked);
+
+ this.setParentAsMarked(data.markAll, isMarked);
+ }
+
+ this.setParentAsMarked(checkbox, checkbox.checked);
+
+ this.saveState(type, [objectId], isMarked);
+ }
+
+ /**
+ * Saves the state for given item object ids.
+ */
+ private saveState(objectType: string, objectIds: number[], isMarked: boolean): void {
+ Ajax.api(this, {
+ actionName: isMarked ? "mark" : "unmark",
+ parameters: {
+ pageClassNames: this.pageClassNames,
+ pageObjectID: this.pageObjectId,
+ objectIDs: objectIds,
+ objectType,
+ },
+ });
+ }
+
+ /**
+ * Executes an editor action.
+ */
+ private executeAction(event: MouseEvent): void {
+ const listItem = event.currentTarget as HTMLLIElement;
+ const data = this.itemData.get(listItem)!;
+
+ if (data.url) {
+ window.location.href = data.url;
+ return;
+ }
+
+ function triggerEvent() {
+ const type = listItem.dataset.type!;
+
+ EventHandler.fire("com.woltlab.wcf.clipboard", type, {
+ data,
+ listItem,
+ responseData: null,
+ });
+ }
+
+ const message = typeof data.internalData.confirmMessage === "string" ? data.internalData.confirmMessage : "";
+ let fireEvent = true;
+
+ if (Core.isPlainObject(data.parameters) && data.parameters.actionName && data.parameters.className) {
+ if (data.parameters.actionName === "unmarkAll" || Array.isArray(data.parameters.objectIDs)) {
+ if (message.length) {
+ const template = typeof data.internalData.template === "string" ? data.internalData.template : "";
+
+ UiConfirmation.show({
+ confirm: () => {
+ const formData = {};
+
+ if (template.length) {
+ UiConfirmation.getContentElement()
+ .querySelectorAll("input, select, textarea")
+ .forEach((item: HTMLInputElement) => {
+ const name = item.name;
+
+ switch (item.nodeName) {
+ case "INPUT":
+ if ((item.type !== "checkbox" && item.type !== "radio") || item.checked) {
+ formData[name] = item.value;
+ }
+ break;
+
+ case "SELECT":
+ formData[name] = item.value;
+ break;
+
+ case "TEXTAREA":
+ formData[name] = item.value.trim();
+ break;
+ }
+ });
+ }
+
+ this._executeProxyAction(listItem, data, formData);
+ },
+ message,
+ template,
+ });
+ } else {
+ this._executeProxyAction(listItem, data);
+ }
+ }
+ } else if (message.length) {
+ fireEvent = false;
+
+ UiConfirmation.show({
+ confirm: triggerEvent,
+ message,
+ });
+ }
+
+ if (fireEvent) {
+ triggerEvent();
+ }
+ }
+
+ /**
+ * Forwards clipboard actions to an individual handler.
+ */
+ private _executeProxyAction(
+ listItem: HTMLLIElement,
+ data: ClipboardActionData,
+ formData: ArbitraryObject = {},
+ ): void {
+ const objectIds = data.parameters.actionName !== "unmarkAll" ? data.parameters.objectIDs : [];
+ const parameters = { data: formData };
+
+ if (Core.isPlainObject(data.internalData.parameters)) {
+ Object.entries(data.internalData.parameters as ArbitraryObject).forEach(([key, value]) => {
+ parameters[key] = value;
+ });
+ }
+
+ Ajax.api(
+ this,
+ {
+ actionName: data.parameters.actionName,
+ className: data.parameters.className,
+ objectIDs: objectIds,
+ parameters,
+ },
+ (responseData: AjaxResponse) => {
+ if (data.actionName !== "unmarkAll") {
+ const type = listItem.dataset.type!;
+
+ EventHandler.fire("com.woltlab.wcf.clipboard", type, {
+ data,
+ listItem,
+ responseData,
+ });
+
+ const reloadPageOnSuccess = this.reloadPageOnSuccess.get(type);
+ if (reloadPageOnSuccess && reloadPageOnSuccess.includes(responseData.actionName)) {
+ window.location.reload();
+ return;
+ }
+ }
+
+ this.loadMarkedItems();
+ },
+ );
+ }
+
+ /**
+ * Unmarks all clipboard items for an object type.
+ */
+ private unmarkAll(event: MouseEvent): void {
+ const listItem = event.currentTarget as HTMLElement;
+
+ Ajax.api(this, {
+ actionName: "unmarkAll",
+ parameters: {
+ objectType: listItem.dataset.type!,
+ },
+ });
+ }
+
+ /**
+ * Sets up ajax request object.
+ */
+ _ajaxSetup(): ReturnType<AjaxCallbackSetup> {
+ return {
+ data: {
+ className: "wcf\\data\\clipboard\\item\\ClipboardItemAction",
+ },
+ };
+ }
+
+ /**
+ * Handles successful AJAX requests.
+ */
+ _ajaxSuccess(data: AjaxResponse): void {
+ if (data.actionName === "unmarkAll") {
+ const objectType = data.returnValues.objectType;
+ this.containers.forEach((containerData) => {
+ if (containerData.element.dataset.type !== objectType) {
+ return;
+ }
+
+ containerData.element.querySelectorAll(".jsMarked").forEach((element) => element.classList.remove("jsMarked"));
+
+ if (containerData.markAll !== null) {
+ containerData.markAll.checked = false;
+
+ this.setParentAsMarked(containerData.markAll, false);
+ }
+
+ Array.from(containerData.checkboxes).forEach((checkbox) => {
+ checkbox.checked = false;
+
+ this.setParentAsMarked(checkbox, false);
+ });
+
+ UiPageAction.remove(`wcfClipboard-${objectType}`);
+ });
+
+ return;
+ }
+
+ this.itemData = new WeakMap<HTMLLIElement, ClipboardActionData>();
+ this.reloadPageOnSuccess.clear();
+
+ // rebuild markings
+ const markings = Core.isPlainObject(data.returnValues.markedItems) ? data.returnValues.markedItems! : {};
+ this.containers.forEach((containerData) => {
+ const typeName = containerData.element.dataset.type!;
+
+ const objectIds = Array.isArray(markings[typeName]) ? markings[typeName] : [];
+ this.rebuildMarkings(containerData, objectIds);
+ });
+
+ const keepEditors: string[] = Object.keys(data.returnValues.items || {});
+
+ // clear editors
+ this.editors.forEach((editor, typeName) => {
+ if (keepEditors.includes(typeName)) {
+ UiPageAction.remove(`wcfClipboard-${typeName}`);
+
+ this.editorDropdowns.get(typeName)!.innerHTML = "";
+ }
+ });
+
+ // no items
+ if (!data.returnValues.items) {
+ return;
+ }
+
+ // rebuild editors
+ Object.entries(data.returnValues.items).forEach(([typeName, typeData]) => {
+ this.reloadPageOnSuccess.set(typeName, typeData.reloadPageOnSuccess);
+
+ let created = false;
+
+ let editor = this.editors.get(typeName);
+ let dropdown = this.editorDropdowns.get(typeName)!;
+ if (editor === undefined) {
+ created = true;
+
+ editor = document.createElement("a");
+ editor.className = "dropdownToggle";
+ editor.textContent = typeData.label;
+
+ this.editors.set(typeName, editor);
+
+ dropdown = document.createElement("ol");
+ dropdown.className = "dropdownMenu";
+
+ this.editorDropdowns.set(typeName, dropdown);
+ } else {
+ editor.textContent = typeData.label;
+ dropdown.innerHTML = "";
+ }
+
+ // create editor items
+ Object.values(typeData.items).forEach((itemData) => {
+ const item = document.createElement("li");
+ const label = document.createElement("span");
+ label.textContent = itemData.label;
+ item.appendChild(label);
+ dropdown.appendChild(item);
+
+ item.dataset.type = typeName;
+ item.addEventListener("click", (ev) => this.executeAction(ev));
+
+ this.itemData.set(item, itemData);
+ });
+
+ const divider = document.createElement("li");
+ divider.classList.add("dropdownDivider");
+ dropdown.appendChild(divider);
+
+ // add 'unmark all'
+ const unmarkAll = document.createElement("li");
+ unmarkAll.dataset.type = typeName;
+ const label = document.createElement("span");
+ label.textContent = Language.get("wcf.clipboard.item.unmarkAll");
+ unmarkAll.appendChild(label);
+ unmarkAll.addEventListener("click", (ev) => this.unmarkAll(ev));
+ dropdown.appendChild(unmarkAll);
+
+ if (keepEditors.indexOf(typeName) !== -1) {
+ const actionName = `wcfClipboard-${typeName}`;
+
+ if (UiPageAction.has(actionName)) {
+ UiPageAction.show(actionName);
+ } else {
+ UiPageAction.add(actionName, editor);
+ }
+ }
+
+ if (created) {
+ const parent = editor.parentElement!;
+ parent.classList.add("dropdown");
+ parent.appendChild(dropdown);
+ UiDropdownSimple.init(editor);
+ }
+ });
+ }
+
+ /**
+ * Rebuilds the mark state for each item.
+ */
+ private rebuildMarkings(data: ContainerData, objectIds: number[]): void {
+ let markAll = true;
+
+ Array.from(data.checkboxes).forEach((checkbox) => {
+ const clipboardObject = checkbox.closest(".jsClipboardObject") as HTMLElement;
+
+ const isMarked = objectIds.includes(~~checkbox.dataset.objectId!);
+ if (!isMarked) {
+ markAll = false;
+ }
+
+ checkbox.checked = isMarked;
+ if (isMarked) {
+ clipboardObject.classList.add("jsMarked");
+ } else {
+ clipboardObject.classList.remove("jsMarked");
+ }
+
+ this.setParentAsMarked(checkbox, isMarked);
+ });
+
+ if (data.markAll !== null) {
+ data.markAll.checked = markAll;
+
+ this.setParentAsMarked(data.markAll, markAll);
+
+ const parent = data.markAll.closest(".columnMark");
+ if (parent) {
+ if (markAll) {
+ parent.classList.add("jsMarked");
+ } else {
+ parent.classList.remove("jsMarked");
+ }
+ }
+ }
+ }
+
+ private setParentAsMarked(element: HTMLElement, isMarked: boolean): void {
+ const parent = element.parentElement!;
+ if (parent.getAttribute("role") === "checkbox") {
+ parent.setAttribute("aria-checked", isMarked ? "true" : "false");
+ }
+ }
+
+ /**
+ * Hides the clipboard editor for the given object type.
+ */
+ hideEditor(objectType: string): void {
+ UiPageAction.remove("wcfClipboard-" + objectType);
+
+ UiScreen.pageOverlayOpen();
+ }
+
+ /**
+ * Shows the clipboard editor.
+ */
+ showEditor(): void {
+ this.loadMarkedItems();
+
+ UiScreen.pageOverlayClose();
+ }
+
+ /**
+ * Unmarks the objects with given clipboard object type and ids.
+ */
+ unmark(objectType: string, objectIds: number[]): void {
+ this.saveState(objectType, objectIds, false);
+ }
+}
+
+let controllerClipboard: ControllerClipboard;
+
+function getControllerClipboard(): ControllerClipboard {
+ if (!controllerClipboard) {
+ controllerClipboard = new ControllerClipboard();
+ }
+
+ return controllerClipboard;
+}
+
+/**
+ * Initializes the clipboard API handler.
+ */
+export function setup(options: ClipboardOptions): void {
+ getControllerClipboard().setup(options);
+}
+
+/**
+ * Reloads the clipboard data.
+ */
+export function reload(): void {
+ getControllerClipboard().reload();
+}
+
+/**
+ * Hides the clipboard editor for the given object type.
+ */
+export function hideEditor(objectType: string): void {
+ getControllerClipboard().hideEditor(objectType);
+}
+
+/**
+ * Shows the clipboard editor.
+ */
+export function showEditor(): void {
+ getControllerClipboard().showEditor();
+}
+
+/**
+ * Unmarks the objects with given clipboard object type and ids.
+ */
+export function unmark(objectType: string, objectIds: number[]): void {
+ getControllerClipboard().unmark(objectType, objectIds);
+}