/**
- * Abstract implementation of the JavaScript component of a form field handling
- * a list of packages.
+ * Abstract implementation of the JavaScript component of a form field handling a list of packages.
*
- * @author Matthias Schmidt
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Acp/Form/Builder/Field/Devtools/Project/AbstractPackageList
- * @since 5.2
+ * @author Matthias Schmidt
+ * @copyright 2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Acp/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
*/
-define(['Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language'], function (DomChangeListener, DomTraverse, DomUtil, EventKey, Language) {
+define(["require", "exports", "tslib", "../../../../../../Core", "../../../../../../Language", "../../../../../../Dom/Traverse", "../../../../../../Dom/Change/Listener", "../../../../../../Dom/Util"], function (require, exports, tslib_1, Core, Language, DomTraverse, Listener_1, Util_1) {
"use strict";
- /**
- * @constructor
- */
- function AbstractPackageList(formFieldId, existingPackages) {
- this.init(formFieldId, existingPackages);
- }
- ;
- AbstractPackageList.prototype = {
- /**
- * Initializes the package list handler.
- *
- * @param {string} formFieldId id of the associated form field
- * @param {object[]} existingPackages data of existing packages
- */
- init: function (formFieldId, existingPackages) {
- this._formFieldId = formFieldId;
- this._packageList = elById(this._formFieldId + '_packageList');
- if (this._packageList === null) {
- throw new Error("Cannot find package list for packages field with id '" + this._formFieldId + "'.");
- }
- this._packageIdentifier = elById(this._formFieldId + '_packageIdentifier');
- if (this._packageIdentifier === null) {
- throw new Error("Cannot find package identifier form field for packages field with id '" + this._formFieldId + "'.");
- }
- this._packageIdentifier.addEventListener('keypress', this._keyPress.bind(this));
- this._addButton = elById(this._formFieldId + '_addButton');
- if (this._addButton === null) {
- throw new Error("Cannot find add button for packages field with id '" + this._formFieldId + "'.");
- }
- this._addButton.addEventListener('click', this._addPackage.bind(this));
- this._form = this._packageList.closest('form');
- if (this._form === null) {
- throw new Error("Cannot find form element for packages field with id '" + this._formFieldId + "'.");
- }
- this._form.addEventListener('submit', this._submit.bind(this));
- existingPackages.forEach(this._addPackageByData.bind(this));
- },
+ Core = tslib_1.__importStar(Core);
+ Language = tslib_1.__importStar(Language);
+ DomTraverse = tslib_1.__importStar(DomTraverse);
+ Listener_1 = tslib_1.__importDefault(Listener_1);
+ Util_1 = tslib_1.__importDefault(Util_1);
+ class AbstractPackageList {
+ constructor(formFieldId, existingPackages) {
+ this.formFieldId = formFieldId;
+ this.packageList = document.getElementById(`${this.formFieldId}_packageList`);
+ if (this.packageList === null) {
+ throw new Error(`Cannot find package list for packages field with id '${this.formFieldId}'.`);
+ }
+ this.packageIdentifier = document.getElementById(`${this.formFieldId}_packageIdentifier`);
+ if (this.packageIdentifier === null) {
+ throw new Error(`Cannot find package identifier form field for packages field with id '${this.formFieldId}'.`);
+ }
+ this.packageIdentifier.addEventListener("keypress", (ev) => this.keyPress(ev));
+ this.addButton = document.getElementById(`${this.formFieldId}_addButton`);
+ if (this.addButton === null) {
+ throw new Error(`Cannot find add button for packages field with id '${this.formFieldId}'.`);
+ }
+ this.addButton.addEventListener("click", (ev) => this.addPackage(ev));
+ this.form = this.packageList.closest("form");
+ if (this.form === null) {
+ throw new Error(`Cannot find form element for packages field with id '${this.formFieldId}'.`);
+ }
+ this.form.addEventListener("submit", () => this.submit());
+ existingPackages.forEach((data) => this.addPackageByData(data));
+ }
/**
- * Adds a package to the package list as a consequence of the given
- * event. If the package data is invalid, an error message is shown
- * and no package is added.
+ * Adds a package to the package list as a consequence of the given event.
*
- * @param {Event} event event that triggered trying to add the package
+ * If the package data is invalid, an error message is shown and no package is added.
*/
- _addPackage: function (event) {
+ addPackage(event) {
event.preventDefault();
event.stopPropagation();
// validate data
- if (!this._validateInput()) {
+ if (!this.validateInput()) {
return;
}
- this._addPackageByData(this._getInputData());
+ this.addPackageByData(this.getInputData());
// empty fields
- this._emptyInput();
- this._packageIdentifier.focus();
- },
+ this.emptyInput();
+ this.packageIdentifier.focus();
+ }
/**
* Adds a package to the package list using the given package data.
- *
- * @param {object} packageData
*/
- _addPackageByData: function (packageData) {
+ addPackageByData(packageData) {
// add package to list
- var listItem = elCreate('li');
- this._populateListItem(listItem, packageData);
+ const listItem = document.createElement("li");
+ this.populateListItem(listItem, packageData);
// add delete button
- var deleteButton = elCreate('span');
- deleteButton.className = 'icon icon16 fa-times pointer jsTooltip';
- elAttr(deleteButton, 'title', Language.get('wcf.global.button.delete'));
- deleteButton.addEventListener('click', this._removePackage.bind(this));
- DomUtil.prepend(deleteButton, listItem);
- this._packageList.appendChild(listItem);
- DomChangeListener.trigger();
- },
+ const deleteButton = document.createElement("span");
+ deleteButton.className = "icon icon16 fa-times pointer jsTooltip";
+ deleteButton.title = Language.get("wcf.global.button.delete");
+ deleteButton.addEventListener("click", (ev) => this.removePackage(ev));
+ listItem.insertAdjacentElement("afterbegin", deleteButton);
+ this.packageList.appendChild(listItem);
+ Listener_1.default.trigger();
+ }
/**
* Creates the hidden fields when the form is submitted.
- *
- * @param {HTMLElement} listElement package list element from the package list
- * @param {int} index package index
*/
- _createSubmitFields: function (listElement, index) {
- var packageIdentifier = elCreate('input');
- elAttr(packageIdentifier, 'type', 'hidden');
- elAttr(packageIdentifier, 'name', this._formFieldId + '[' + index + '][packageIdentifier]');
- packageIdentifier.value = elData(listElement, 'package-identifier');
- this._form.appendChild(packageIdentifier);
- },
+ createSubmitFields(listElement, index) {
+ const packageIdentifier = document.createElement("input");
+ packageIdentifier.type = "hidden";
+ packageIdentifier.name = `${this.formFieldId}[${index}][packageIdentifier]`;
+ packageIdentifier.value = listElement.dataset.packageIdentifier;
+ this.form.appendChild(packageIdentifier);
+ }
/**
* Empties the input fields.
*/
- _emptyInput: function () {
- this._packageIdentifier.value = '';
- },
- /**
- * Returns the error element for the given form field element.
- * If `createIfNonExistent` is not given or `false`, `null` is returned
- * if there is no error element, otherwise an empty error element
- * is created and returned.
- *
- * @param {?boolean} createIfNonExistent
- * @return {?HTMLElement}
- */
- _getErrorElement: function (element, createIfNoNExistent) {
- var error = DomTraverse.nextByClass(element, 'innerError');
- if (error === null && createIfNoNExistent) {
- error = elCreate('small');
- error.className = 'innerError';
- DomUtil.insertAfter(error, element);
- }
- return error;
- },
+ emptyInput() {
+ this.packageIdentifier.value = "";
+ }
/**
* Returns the current data of the input fields to add a new package.
- *
- * @return {object}
*/
- _getInputData: function () {
+ getInputData() {
return {
- packageIdentifier: this._packageIdentifier.value
+ packageIdentifier: this.packageIdentifier.value,
};
- },
- /**
- * Returns the error element for the package identifier form field.
- * If `createIfNonExistent` is not given or `false`, `null` is returned
- * if there is no error element, otherwise an empty error element
- * is created and returned.
- *
- * @param {?boolean} createIfNonExistent
- * @return {?HTMLElement}
- */
- _getPackageIdentifierErrorElement: function (createIfNonExistent) {
- return this._getErrorElement(this._packageIdentifier, createIfNonExistent);
- },
+ }
/**
- * Adds a package to the package list after pressing ENTER in a
- * text field.
- *
- * @param {Event} event
+ * Adds a package to the package list after pressing ENTER in a text field.
*/
- _keyPress: function (event) {
- if (EventKey.Enter(event)) {
- this._addPackage(event);
+ keyPress(event) {
+ if (event.key === "Enter") {
+ this.addPackage(event);
}
- },
+ }
/**
* Adds all necessary package-relavant data to the given list item.
- *
- * @param {HTMLElement} listItem package list element holding package data
- * @param {object} packageData package data
*/
- _populateListItem: function (listItem, packageData) {
- elData(listItem, 'package-identifier', packageData.packageIdentifier);
- },
+ populateListItem(listItem, packageData) {
+ listItem.dataset.packageIdentifier = packageData.packageIdentifier;
+ }
/**
* Removes a package by clicking on its delete button.
- *
- * @param {Event} event delete button click event
*/
- _removePackage: function (event) {
- elRemove(event.currentTarget.closest('li'));
+ removePackage(event) {
+ event.currentTarget.closest("li").remove();
// remove field errors if the last package has been deleted
- if (!this._packageList.childElementCount &&
- this._packageList.nextElementSibling.tagName === 'SMALL' &&
- this._packageList.nextElementSibling.classList.contains('innerError')) {
- elRemove(this._packageList.nextElementSibling);
- }
- },
+ Util_1.default.innerError(this.packageList, "");
+ }
/**
- * Adds all necessary (hidden) form fields to the form when
- * submitting the form.
+ * Adds all necessary (hidden) form fields to the form when submitting the form.
*/
- _submit: function () {
- DomTraverse.childrenByTag(this._packageList, 'LI').forEach(this._createSubmitFields.bind(this));
- },
+ submit() {
+ DomTraverse.childrenByTag(this.packageList, "LI").forEach((listItem, index) => this.createSubmitFields(listItem, index));
+ }
/**
- * Returns `true` if the currently entered package data is valid.
- * Otherwise `false` is returned and relevant error messages are
- * shown.
- *
- * @return {boolean}
+ * Returns `true` if the currently entered package data is valid. Otherwise `false` is returned and relevant error
+ * messages are shown.
*/
- _validateInput: function () {
- return this._validatePackageIdentifier();
- },
+ validateInput() {
+ return this.validatePackageIdentifier();
+ }
/**
- * Returns `true` if the currently entered package identifier is
- * valid. Otherwise `false` is returned and an error message is
- * shown.
- *
- * @return {boolean}
+ * Returns `true` if the currently entered package identifier is valid. Otherwise `false` is returned and an error
+ * message is shown.
*/
- _validatePackageIdentifier: function () {
- var packageIdentifier = this._packageIdentifier.value;
- if (packageIdentifier === '') {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.global.form.error.empty');
+ validatePackageIdentifier() {
+ const packageIdentifier = this.packageIdentifier.value;
+ if (packageIdentifier === "") {
+ Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.global.form.error.empty"));
return false;
}
if (packageIdentifier.length < 3) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.minimumLength');
+ Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.minimumLength"));
return false;
}
else if (packageIdentifier.length > 191) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.maximumLength');
+ Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.maximumLength"));
return false;
}
- // see `wcf\data\package\Package::isValidPackageName()`
- if (!packageIdentifier.match(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/)) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.format');
+ if (!AbstractPackageList.packageIdentifierRegExp.test(packageIdentifier)) {
+ Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.format"));
return false;
}
// check if package has already been added
- var duplicate = false;
- DomTraverse.childrenByTag(this._packageList, 'LI').forEach(function (listItem, index) {
- if (elData(listItem, 'package-identifier') === packageIdentifier) {
- duplicate = true;
- }
- });
+ const duplicate = DomTraverse.childrenByTag(this.packageList, "LI").some((listItem) => listItem.dataset.packageIdentifier === packageIdentifier);
if (duplicate) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.duplicate');
+ Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.duplicate"));
return false;
}
// remove outdated errors
- var error = this._getPackageIdentifierErrorElement();
- if (error !== null) {
- elRemove(error);
- }
+ Util_1.default.innerError(this.packageIdentifier, "");
return true;
- },
+ }
/**
- * Returns `true` if the given version is valid. Otherwise `false`
- * is returned and an error message is shown.
- *
- * @param {string} version validated version
- * @param {function} versionErrorGetter returns the version error element
- * @return {boolean}
+ * Returns `true` if the given version is valid. Otherwise `false` is returned and an error message is shown.
*/
- _validateVersion: function (version, versionErrorGetter) {
+ validateVersion(versionElement) {
+ const version = versionElement.value;
// see `wcf\data\package\Package::isValidVersion()`
// the version is no a required attribute
- if (version !== '') {
+ if (version !== "") {
if (version.length > 255) {
- versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.maximumLength');
+ Util_1.default.innerError(versionElement, Language.get("wcf.acp.devtools.project.packageVersion.error.maximumLength"));
return false;
}
- // see `wcf\data\package\Package::isValidVersion()`
- if (!version.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
- versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
+ if (!AbstractPackageList.versionRegExp.test(version)) {
+ Util_1.default.innerError(versionElement, Language.get("wcf.acp.devtools.project.packageVersion.error.format"));
return false;
}
}
// remove outdated errors
- var error = versionErrorGetter();
- if (error !== null) {
- elRemove(error);
- }
+ Util_1.default.innerError(versionElement, "");
return true;
}
- };
+ }
+ // see `wcf\data\package\Package::isValidPackageName()`
+ AbstractPackageList.packageIdentifierRegExp = new RegExp(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/);
+ // see `wcf\data\package\Package::isValidVersion()`
+ AbstractPackageList.versionRegExp = new RegExp(/^([0-9]+).([0-9]+)\.([0-9]+)( (a|alpha|b|beta|d|dev|rc|pl) ([0-9]+))?$/i);
+ Core.enableLegacyInheritance(AbstractPackageList);
return AbstractPackageList;
});
+++ /dev/null
-/**
- * Abstract implementation of the JavaScript component of a form field handling
- * a list of packages.
- *
- * @author Matthias Schmidt
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Acp/Form/Builder/Field/Devtools/Project/AbstractPackageList
- * @since 5.2
- */
-define(['Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language'], function(DomChangeListener, DomTraverse, DomUtil, EventKey, Language) {
- "use strict";
-
- /**
- * @constructor
- */
- function AbstractPackageList(formFieldId, existingPackages) {
- this.init(formFieldId, existingPackages);
- };
- AbstractPackageList.prototype = {
- /**
- * Initializes the package list handler.
- *
- * @param {string} formFieldId id of the associated form field
- * @param {object[]} existingPackages data of existing packages
- */
- init: function(formFieldId, existingPackages) {
- this._formFieldId = formFieldId;
-
- this._packageList = elById(this._formFieldId + '_packageList');
- if (this._packageList === null) {
- throw new Error("Cannot find package list for packages field with id '" + this._formFieldId + "'.");
- }
-
- this._packageIdentifier = elById(this._formFieldId + '_packageIdentifier');
- if (this._packageIdentifier === null) {
- throw new Error("Cannot find package identifier form field for packages field with id '" + this._formFieldId + "'.");
- }
- this._packageIdentifier.addEventListener('keypress', this._keyPress.bind(this));
-
- this._addButton = elById(this._formFieldId + '_addButton');
- if (this._addButton === null) {
- throw new Error("Cannot find add button for packages field with id '" + this._formFieldId + "'.");
- }
- this._addButton.addEventListener('click', this._addPackage.bind(this));
-
- this._form = this._packageList.closest('form');
- if (this._form === null) {
- throw new Error("Cannot find form element for packages field with id '" + this._formFieldId + "'.");
- }
- this._form.addEventListener('submit', this._submit.bind(this));
-
- existingPackages.forEach(this._addPackageByData.bind(this));
- },
-
- /**
- * Adds a package to the package list as a consequence of the given
- * event. If the package data is invalid, an error message is shown
- * and no package is added.
- *
- * @param {Event} event event that triggered trying to add the package
- */
- _addPackage: function(event) {
- event.preventDefault();
- event.stopPropagation();
-
- // validate data
- if (!this._validateInput()) {
- return;
- }
-
- this._addPackageByData(this._getInputData());
-
- // empty fields
- this._emptyInput();
-
- this._packageIdentifier.focus();
- },
-
- /**
- * Adds a package to the package list using the given package data.
- *
- * @param {object} packageData
- */
- _addPackageByData: function(packageData) {
- // add package to list
- var listItem = elCreate('li');
- this._populateListItem(listItem, packageData);
-
- // add delete button
- var deleteButton = elCreate('span');
- deleteButton.className = 'icon icon16 fa-times pointer jsTooltip';
- elAttr(deleteButton, 'title', Language.get('wcf.global.button.delete'));
- deleteButton.addEventListener('click', this._removePackage.bind(this));
- DomUtil.prepend(deleteButton, listItem);
-
- this._packageList.appendChild(listItem);
-
- DomChangeListener.trigger();
- },
-
- /**
- * Creates the hidden fields when the form is submitted.
- *
- * @param {HTMLElement} listElement package list element from the package list
- * @param {int} index package index
- */
- _createSubmitFields: function(listElement, index) {
- var packageIdentifier = elCreate('input');
- elAttr(packageIdentifier, 'type', 'hidden');
- elAttr(packageIdentifier, 'name', this._formFieldId + '[' + index + '][packageIdentifier]')
- packageIdentifier.value = elData(listElement, 'package-identifier');
- this._form.appendChild(packageIdentifier);
- },
-
- /**
- * Empties the input fields.
- */
- _emptyInput: function() {
- this._packageIdentifier.value = '';
- },
-
- /**
- * Returns the error element for the given form field element.
- * If `createIfNonExistent` is not given or `false`, `null` is returned
- * if there is no error element, otherwise an empty error element
- * is created and returned.
- *
- * @param {?boolean} createIfNonExistent
- * @return {?HTMLElement}
- */
- _getErrorElement: function(element, createIfNoNExistent) {
- var error = DomTraverse.nextByClass(element, 'innerError');
-
- if (error === null && createIfNoNExistent) {
- error = elCreate('small');
- error.className = 'innerError';
-
- DomUtil.insertAfter(error, element);
- }
-
- return error;
- },
-
- /**
- * Returns the current data of the input fields to add a new package.
- *
- * @return {object}
- */
- _getInputData: function() {
- return {
- packageIdentifier: this._packageIdentifier.value
- };
- },
-
- /**
- * Returns the error element for the package identifier form field.
- * If `createIfNonExistent` is not given or `false`, `null` is returned
- * if there is no error element, otherwise an empty error element
- * is created and returned.
- *
- * @param {?boolean} createIfNonExistent
- * @return {?HTMLElement}
- */
- _getPackageIdentifierErrorElement: function(createIfNonExistent) {
- return this._getErrorElement(this._packageIdentifier, createIfNonExistent);
- },
-
- /**
- * Adds a package to the package list after pressing ENTER in a
- * text field.
- *
- * @param {Event} event
- */
- _keyPress: function(event) {
- if (EventKey.Enter(event)) {
- this._addPackage(event);
- }
- },
-
- /**
- * Adds all necessary package-relavant data to the given list item.
- *
- * @param {HTMLElement} listItem package list element holding package data
- * @param {object} packageData package data
- */
- _populateListItem: function(listItem, packageData) {
- elData(listItem, 'package-identifier', packageData.packageIdentifier);
- },
-
- /**
- * Removes a package by clicking on its delete button.
- *
- * @param {Event} event delete button click event
- */
- _removePackage: function(event) {
- elRemove(event.currentTarget.closest('li'));
-
- // remove field errors if the last package has been deleted
- if (
- !this._packageList.childElementCount &&
- this._packageList.nextElementSibling.tagName === 'SMALL' &&
- this._packageList.nextElementSibling.classList.contains('innerError')
- ) {
- elRemove(this._packageList.nextElementSibling);
- }
- },
-
- /**
- * Adds all necessary (hidden) form fields to the form when
- * submitting the form.
- */
- _submit: function() {
- DomTraverse.childrenByTag(this._packageList, 'LI').forEach(this._createSubmitFields.bind(this));
- },
-
- /**
- * Returns `true` if the currently entered package data is valid.
- * Otherwise `false` is returned and relevant error messages are
- * shown.
- *
- * @return {boolean}
- */
- _validateInput: function() {
- return this._validatePackageIdentifier();
- },
-
- /**
- * Returns `true` if the currently entered package identifier is
- * valid. Otherwise `false` is returned and an error message is
- * shown.
- *
- * @return {boolean}
- */
- _validatePackageIdentifier: function() {
- var packageIdentifier = this._packageIdentifier.value;
-
- if (packageIdentifier === '') {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.global.form.error.empty');
-
- return false;
- }
-
- if (packageIdentifier.length < 3) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.minimumLength');
-
- return false;
- }
- else if (packageIdentifier.length > 191) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.maximumLength');
-
- return false;
- }
-
- // see `wcf\data\package\Package::isValidPackageName()`
- if (!packageIdentifier.match(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/)) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.format');
-
- return false;
- }
-
- // check if package has already been added
- var duplicate = false;
- DomTraverse.childrenByTag(this._packageList, 'LI').forEach(function(listItem, index) {
- if (elData(listItem, 'package-identifier') === packageIdentifier) {
- duplicate = true;
- }
- });
-
- if (duplicate) {
- this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.duplicate');
-
- return false;
- }
-
- // remove outdated errors
- var error = this._getPackageIdentifierErrorElement();
- if (error !== null) {
- elRemove(error);
- }
-
- return true;
- },
-
- /**
- * Returns `true` if the given version is valid. Otherwise `false`
- * is returned and an error message is shown.
- *
- * @param {string} version validated version
- * @param {function} versionErrorGetter returns the version error element
- * @return {boolean}
- */
- _validateVersion: function(version, versionErrorGetter) {
- // see `wcf\data\package\Package::isValidVersion()`
- // the version is no a required attribute
- if (version !== '') {
- if (version.length > 255) {
- versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.maximumLength');
-
- return false;
- }
-
- // see `wcf\data\package\Package::isValidVersion()`
- if (!version.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
- versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
-
- return false;
- }
- }
-
- // remove outdated errors
- var error = versionErrorGetter();
- if (error !== null) {
- elRemove(error);
- }
-
- return true;
- }
- };
-
- return AbstractPackageList;
-});
--- /dev/null
+/**
+ * Abstract implementation of the JavaScript component of a form field handling a list of packages.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Acp/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+
+import * as Core from "../../../../../../Core";
+import * as Language from "../../../../../../Language";
+import * as DomTraverse from "../../../../../../Dom/Traverse";
+import DomChangeListener from "../../../../../../Dom/Change/Listener";
+import DomUtil from "../../../../../../Dom/Util";
+import { PackageData } from "./Data";
+
+abstract class AbstractPackageList<TPackageData extends PackageData = PackageData> {
+ protected readonly addButton: HTMLAnchorElement;
+ protected readonly form: HTMLFormElement;
+ protected readonly formFieldId: string;
+ protected readonly packageList: HTMLOListElement;
+ protected readonly packageIdentifier: HTMLInputElement;
+
+ // see `wcf\data\package\Package::isValidPackageName()`
+ protected static packageIdentifierRegExp = new RegExp(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/);
+
+ // see `wcf\data\package\Package::isValidVersion()`
+ protected static versionRegExp = new RegExp(
+ /^([0-9]+).([0-9]+)\.([0-9]+)( (a|alpha|b|beta|d|dev|rc|pl) ([0-9]+))?$/i,
+ );
+
+ constructor(formFieldId: string, existingPackages: TPackageData[]) {
+ this.formFieldId = formFieldId;
+
+ this.packageList = document.getElementById(`${this.formFieldId}_packageList`) as HTMLOListElement;
+ if (this.packageList === null) {
+ throw new Error(`Cannot find package list for packages field with id '${this.formFieldId}'.`);
+ }
+
+ this.packageIdentifier = document.getElementById(`${this.formFieldId}_packageIdentifier`) as HTMLInputElement;
+ if (this.packageIdentifier === null) {
+ throw new Error(`Cannot find package identifier form field for packages field with id '${this.formFieldId}'.`);
+ }
+ this.packageIdentifier.addEventListener("keypress", (ev) => this.keyPress(ev));
+
+ this.addButton = document.getElementById(`${this.formFieldId}_addButton`) as HTMLAnchorElement;
+ if (this.addButton === null) {
+ throw new Error(`Cannot find add button for packages field with id '${this.formFieldId}'.`);
+ }
+ this.addButton.addEventListener("click", (ev) => this.addPackage(ev));
+
+ this.form = this.packageList.closest("form") as HTMLFormElement;
+ if (this.form === null) {
+ throw new Error(`Cannot find form element for packages field with id '${this.formFieldId}'.`);
+ }
+ this.form.addEventListener("submit", () => this.submit());
+
+ existingPackages.forEach((data) => this.addPackageByData(data));
+ }
+
+ /**
+ * Adds a package to the package list as a consequence of the given event.
+ *
+ * If the package data is invalid, an error message is shown and no package is added.
+ */
+ protected addPackage(event: Event): void {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // validate data
+ if (!this.validateInput()) {
+ return;
+ }
+
+ this.addPackageByData(this.getInputData());
+
+ // empty fields
+ this.emptyInput();
+
+ this.packageIdentifier.focus();
+ }
+
+ /**
+ * Adds a package to the package list using the given package data.
+ */
+ protected addPackageByData(packageData: TPackageData): void {
+ // add package to list
+ const listItem = document.createElement("li");
+ this.populateListItem(listItem, packageData);
+
+ // add delete button
+ const deleteButton = document.createElement("span");
+ deleteButton.className = "icon icon16 fa-times pointer jsTooltip";
+ deleteButton.title = Language.get("wcf.global.button.delete");
+ deleteButton.addEventListener("click", (ev) => this.removePackage(ev));
+ listItem.insertAdjacentElement("afterbegin", deleteButton);
+
+ this.packageList.appendChild(listItem);
+
+ DomChangeListener.trigger();
+ }
+
+ /**
+ * Creates the hidden fields when the form is submitted.
+ */
+ protected createSubmitFields(listElement: HTMLLIElement, index: number): void {
+ const packageIdentifier = document.createElement("input");
+ packageIdentifier.type = "hidden";
+ packageIdentifier.name = `${this.formFieldId}[${index}][packageIdentifier]`;
+ packageIdentifier.value = listElement.dataset.packageIdentifier!;
+ this.form.appendChild(packageIdentifier);
+ }
+
+ /**
+ * Empties the input fields.
+ */
+ protected emptyInput(): void {
+ this.packageIdentifier.value = "";
+ }
+
+ /**
+ * Returns the current data of the input fields to add a new package.
+ */
+ protected getInputData(): TPackageData {
+ return {
+ packageIdentifier: this.packageIdentifier.value,
+ } as TPackageData;
+ }
+
+ /**
+ * Adds a package to the package list after pressing ENTER in a text field.
+ */
+ protected keyPress(event: KeyboardEvent): void {
+ if (event.key === "Enter") {
+ this.addPackage(event);
+ }
+ }
+
+ /**
+ * Adds all necessary package-relavant data to the given list item.
+ */
+ protected populateListItem(listItem: HTMLLIElement, packageData: TPackageData): void {
+ listItem.dataset.packageIdentifier = packageData.packageIdentifier;
+ }
+
+ /**
+ * Removes a package by clicking on its delete button.
+ */
+ protected removePackage(event: Event): void {
+ (event.currentTarget as HTMLElement).closest("li")!.remove();
+
+ // remove field errors if the last package has been deleted
+ DomUtil.innerError(this.packageList, "");
+ }
+
+ /**
+ * Adds all necessary (hidden) form fields to the form when submitting the form.
+ */
+ protected submit(): void {
+ DomTraverse.childrenByTag(this.packageList, "LI").forEach((listItem, index) =>
+ this.createSubmitFields(listItem, index),
+ );
+ }
+
+ /**
+ * Returns `true` if the currently entered package data is valid. Otherwise `false` is returned and relevant error
+ * messages are shown.
+ */
+ protected validateInput(): boolean {
+ return this.validatePackageIdentifier();
+ }
+
+ /**
+ * Returns `true` if the currently entered package identifier is valid. Otherwise `false` is returned and an error
+ * message is shown.
+ */
+ protected validatePackageIdentifier(): boolean {
+ const packageIdentifier = this.packageIdentifier.value;
+
+ if (packageIdentifier === "") {
+ DomUtil.innerError(this.packageIdentifier, Language.get("wcf.global.form.error.empty"));
+
+ return false;
+ }
+
+ if (packageIdentifier.length < 3) {
+ DomUtil.innerError(
+ this.packageIdentifier,
+ Language.get("wcf.acp.devtools.project.packageIdentifier.error.minimumLength"),
+ );
+
+ return false;
+ } else if (packageIdentifier.length > 191) {
+ DomUtil.innerError(
+ this.packageIdentifier,
+ Language.get("wcf.acp.devtools.project.packageIdentifier.error.maximumLength"),
+ );
+
+ return false;
+ }
+
+ if (!AbstractPackageList.packageIdentifierRegExp.test(packageIdentifier)) {
+ DomUtil.innerError(
+ this.packageIdentifier,
+ Language.get("wcf.acp.devtools.project.packageIdentifier.error.format"),
+ );
+
+ return false;
+ }
+
+ // check if package has already been added
+ const duplicate = DomTraverse.childrenByTag(this.packageList, "LI").some(
+ (listItem) => listItem.dataset.packageIdentifier === packageIdentifier,
+ );
+
+ if (duplicate) {
+ DomUtil.innerError(
+ this.packageIdentifier,
+ Language.get("wcf.acp.devtools.project.packageIdentifier.error.duplicate"),
+ );
+
+ return false;
+ }
+
+ // remove outdated errors
+ DomUtil.innerError(this.packageIdentifier, "");
+
+ return true;
+ }
+
+ /**
+ * Returns `true` if the given version is valid. Otherwise `false` is returned and an error message is shown.
+ */
+ protected validateVersion(versionElement: HTMLInputElement): boolean {
+ const version = versionElement.value;
+
+ // see `wcf\data\package\Package::isValidVersion()`
+ // the version is no a required attribute
+ if (version !== "") {
+ if (version.length > 255) {
+ DomUtil.innerError(versionElement, Language.get("wcf.acp.devtools.project.packageVersion.error.maximumLength"));
+
+ return false;
+ }
+
+ if (!AbstractPackageList.versionRegExp.test(version)) {
+ DomUtil.innerError(versionElement, Language.get("wcf.acp.devtools.project.packageVersion.error.format"));
+
+ return false;
+ }
+ }
+
+ // remove outdated errors
+ DomUtil.innerError(versionElement, "");
+
+ return true;
+ }
+}
+
+Core.enableLegacyInheritance(AbstractPackageList);
+
+export = AbstractPackageList;