05a71361362058be1ab3ad4ae5f0063bbc0870c4
[GitHub/WoltLab/WCF.git] /
1 /**
2 * Abstract implementation of the JavaScript component of a form field handling a list of packages.
3 *
4 * @author Matthias Schmidt
5 * @copyright 2001-2021 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 * @module WoltLabSuite/Core/Acp/Form/Builder/Field/Devtools/Project/AbstractPackageList
8 * @since 5.2
9 */
10 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) {
11 "use strict";
12 Core = tslib_1.__importStar(Core);
13 Language = tslib_1.__importStar(Language);
14 DomTraverse = tslib_1.__importStar(DomTraverse);
15 Listener_1 = tslib_1.__importDefault(Listener_1);
16 Util_1 = tslib_1.__importDefault(Util_1);
17 class AbstractPackageList {
18 constructor(formFieldId, existingPackages) {
19 this.formFieldId = formFieldId;
20 this.packageList = document.getElementById(`${this.formFieldId}_packageList`);
21 if (this.packageList === null) {
22 throw new Error(`Cannot find package list for packages field with id '${this.formFieldId}'.`);
23 }
24 this.packageIdentifier = document.getElementById(`${this.formFieldId}_packageIdentifier`);
25 if (this.packageIdentifier === null) {
26 throw new Error(`Cannot find package identifier form field for packages field with id '${this.formFieldId}'.`);
27 }
28 this.packageIdentifier.addEventListener("keypress", (ev) => this.keyPress(ev));
29 this.addButton = document.getElementById(`${this.formFieldId}_addButton`);
30 if (this.addButton === null) {
31 throw new Error(`Cannot find add button for packages field with id '${this.formFieldId}'.`);
32 }
33 this.addButton.addEventListener("click", (ev) => this.addPackage(ev));
34 this.form = this.packageList.closest("form");
35 if (this.form === null) {
36 throw new Error(`Cannot find form element for packages field with id '${this.formFieldId}'.`);
37 }
38 this.form.addEventListener("submit", () => this.submit());
39 existingPackages.forEach((data) => this.addPackageByData(data));
40 }
41 /**
42 * Adds a package to the package list as a consequence of the given event.
43 *
44 * If the package data is invalid, an error message is shown and no package is added.
45 */
46 addPackage(event) {
47 event.preventDefault();
48 event.stopPropagation();
49 // validate data
50 if (!this.validateInput()) {
51 return;
52 }
53 this.addPackageByData(this.getInputData());
54 // empty fields
55 this.emptyInput();
56 this.packageIdentifier.focus();
57 }
58 /**
59 * Adds a package to the package list using the given package data.
60 */
61 addPackageByData(packageData) {
62 // add package to list
63 const listItem = document.createElement("li");
64 this.populateListItem(listItem, packageData);
65 // add delete button
66 const deleteButton = document.createElement("span");
67 deleteButton.className = "icon icon16 fa-times pointer jsTooltip";
68 deleteButton.title = Language.get("wcf.global.button.delete");
69 deleteButton.addEventListener("click", (ev) => this.removePackage(ev));
70 listItem.insertAdjacentElement("afterbegin", deleteButton);
71 this.packageList.appendChild(listItem);
72 Listener_1.default.trigger();
73 }
74 /**
75 * Creates the hidden fields when the form is submitted.
76 */
77 createSubmitFields(listElement, index) {
78 const packageIdentifier = document.createElement("input");
79 packageIdentifier.type = "hidden";
80 packageIdentifier.name = `${this.formFieldId}[${index}][packageIdentifier]`;
81 packageIdentifier.value = listElement.dataset.packageIdentifier;
82 this.form.appendChild(packageIdentifier);
83 }
84 /**
85 * Empties the input fields.
86 */
87 emptyInput() {
88 this.packageIdentifier.value = "";
89 }
90 /**
91 * Returns the current data of the input fields to add a new package.
92 */
93 getInputData() {
94 return {
95 packageIdentifier: this.packageIdentifier.value,
96 };
97 }
98 /**
99 * Adds a package to the package list after pressing ENTER in a text field.
100 */
101 keyPress(event) {
102 if (event.key === "Enter") {
103 this.addPackage(event);
104 }
105 }
106 /**
107 * Adds all necessary package-relavant data to the given list item.
108 */
109 populateListItem(listItem, packageData) {
110 listItem.dataset.packageIdentifier = packageData.packageIdentifier;
111 }
112 /**
113 * Removes a package by clicking on its delete button.
114 */
115 removePackage(event) {
116 event.currentTarget.closest("li").remove();
117 // remove field errors if the last package has been deleted
118 Util_1.default.innerError(this.packageList, "");
119 }
120 /**
121 * Adds all necessary (hidden) form fields to the form when submitting the form.
122 */
123 submit() {
124 DomTraverse.childrenByTag(this.packageList, "LI").forEach((listItem, index) => this.createSubmitFields(listItem, index));
125 }
126 /**
127 * Returns `true` if the currently entered package data is valid. Otherwise `false` is returned and relevant error
128 * messages are shown.
129 */
130 validateInput() {
131 return this.validatePackageIdentifier();
132 }
133 /**
134 * Returns `true` if the currently entered package identifier is valid. Otherwise `false` is returned and an error
135 * message is shown.
136 */
137 validatePackageIdentifier() {
138 const packageIdentifier = this.packageIdentifier.value;
139 if (packageIdentifier === "") {
140 Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.global.form.error.empty"));
141 return false;
142 }
143 if (packageIdentifier.length < 3) {
144 Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.minimumLength"));
145 return false;
146 }
147 else if (packageIdentifier.length > 191) {
148 Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.maximumLength"));
149 return false;
150 }
151 if (!AbstractPackageList.packageIdentifierRegExp.test(packageIdentifier)) {
152 Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.format"));
153 return false;
154 }
155 // check if package has already been added
156 const duplicate = DomTraverse.childrenByTag(this.packageList, "LI").some((listItem) => listItem.dataset.packageIdentifier === packageIdentifier);
157 if (duplicate) {
158 Util_1.default.innerError(this.packageIdentifier, Language.get("wcf.acp.devtools.project.packageIdentifier.error.duplicate"));
159 return false;
160 }
161 // remove outdated errors
162 Util_1.default.innerError(this.packageIdentifier, "");
163 return true;
164 }
165 /**
166 * Returns `true` if the given version is valid. Otherwise `false` is returned and an error message is shown.
167 */
168 validateVersion(versionElement) {
169 const version = versionElement.value;
170 // see `wcf\data\package\Package::isValidVersion()`
171 // the version is no a required attribute
172 if (version !== "") {
173 if (version.length > 255) {
174 Util_1.default.innerError(versionElement, Language.get("wcf.acp.devtools.project.packageVersion.error.maximumLength"));
175 return false;
176 }
177 if (!AbstractPackageList.versionRegExp.test(version)) {
178 Util_1.default.innerError(versionElement, Language.get("wcf.acp.devtools.project.packageVersion.error.format"));
179 return false;
180 }
181 }
182 // remove outdated errors
183 Util_1.default.innerError(versionElement, "");
184 return true;
185 }
186 }
187 // see `wcf\data\package\Package::isValidPackageName()`
188 AbstractPackageList.packageIdentifierRegExp = new RegExp(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/);
189 // see `wcf\data\package\Package::isValidVersion()`
190 AbstractPackageList.versionRegExp = new RegExp(/^([0-9]+).([0-9]+)\.([0-9]+)( (a|alpha|b|beta|d|dev|rc|pl) ([0-9]+))?$/i);
191 Core.enableLegacyInheritance(AbstractPackageList);
192 return AbstractPackageList;
193 });