Merge branch '6.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WoltLabSuite / Core / Core.js
1 /**
2 * Provides the basic core functionality.
3 *
4 * @author Alexander Ebert
5 * @copyright 2001-2019 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 */
8 define(["require", "exports"], function (require, exports) {
9 "use strict";
10 Object.defineProperty(exports, "__esModule", { value: true });
11 exports.clone = clone;
12 exports.convertLegacyUrl = convertLegacyUrl;
13 exports.extend = extend;
14 exports.inherit = inherit;
15 exports.isPlainObject = isPlainObject;
16 exports.getType = getType;
17 exports.getUuid = getUuid;
18 exports.serialize = serialize;
19 exports.triggerEvent = triggerEvent;
20 exports.getStoragePrefix = getStoragePrefix;
21 exports.stringToBool = stringToBool;
22 exports.debounce = debounce;
23 exports.enableLegacyInheritance = enableLegacyInheritance;
24 exports.getXsrfToken = getXsrfToken;
25 const _clone = function (variable) {
26 if (typeof variable === "object" && (Array.isArray(variable) || isPlainObject(variable))) {
27 return _cloneObject(variable);
28 }
29 return variable;
30 };
31 const _cloneObject = function (obj) {
32 if (!obj) {
33 return null;
34 }
35 if (Array.isArray(obj)) {
36 return obj.slice();
37 }
38 const newObj = {};
39 Object.keys(obj).forEach((key) => (newObj[key] = _clone(obj[key])));
40 return newObj;
41 };
42 const _prefix = "wsc" + window.WCF_PATH.hashCode() + "-";
43 /**
44 * Deep clones an object.
45 */
46 function clone(obj) {
47 return _clone(obj);
48 }
49 /**
50 * Converts WCF 2.0-style URLs into the default URL layout.
51 */
52 function convertLegacyUrl(url) {
53 return url.replace(/^index\.php\/(.*?)\/\?/, (match, controller) => {
54 const parts = controller.split(/([A-Z][a-z0-9]+)/);
55 controller = "";
56 for (let i = 0, length = parts.length; i < length; i++) {
57 const part = parts[i].trim();
58 if (part.length) {
59 if (controller.length) {
60 controller += "-";
61 }
62 controller += part.toLowerCase();
63 }
64 }
65 return `index.php?${controller}/&`;
66 });
67 }
68 /**
69 * Merges objects with the first argument.
70 *
71 * @param {object} out destination object
72 * @param {...object} args variable number of objects to be merged into the destination object
73 * @return {object} destination object with all provided objects merged into
74 */
75 function extend(out, ...args) {
76 out = out || {};
77 const newObj = clone(out);
78 for (let i = 0, length = args.length; i < length; i++) {
79 const obj = args[i];
80 if (!obj) {
81 continue;
82 }
83 Object.keys(obj).forEach((key) => {
84 if (!Array.isArray(obj[key]) && typeof obj[key] === "object") {
85 if (isPlainObject(obj[key])) {
86 // object literals have the prototype of Object which in return has no parent prototype
87 newObj[key] = extend(out[key], obj[key]);
88 }
89 else {
90 newObj[key] = obj[key];
91 }
92 }
93 else {
94 newObj[key] = obj[key];
95 }
96 });
97 }
98 return newObj;
99 }
100 /**
101 * Inherits the prototype methods from one constructor to another
102 * constructor.
103 *
104 * Usage:
105 *
106 * function MyDerivedClass() {}
107 * Core.inherit(MyDerivedClass, TheAwesomeBaseClass, {
108 * // regular prototype for `MyDerivedClass`
109 *
110 * overwrittenMethodFromBaseClass: function(foo, bar) {
111 * // do stuff
112 *
113 * // invoke parent
114 * MyDerivedClass._super.prototype.overwrittenMethodFromBaseClass.call(this, foo, bar);
115 * }
116 * });
117 *
118 * @see https://github.com/nodejs/node/blob/7d14dd9b5e78faabb95d454a79faa513d0bbc2a5/lib/util.js#L697-L735
119 * @deprecated 5.4 Use the native `class` and `extends` keywords instead.
120 */
121 function inherit(constructor, superConstructor, propertiesObject) {
122 if (constructor === undefined || constructor === null) {
123 throw new TypeError("The constructor must not be undefined or null.");
124 }
125 if (superConstructor === undefined || superConstructor === null) {
126 throw new TypeError("The super constructor must not be undefined or null.");
127 }
128 if (superConstructor.prototype === undefined) {
129 throw new TypeError("The super constructor must have a prototype.");
130 }
131 constructor._super = superConstructor;
132 constructor.prototype = extend(Object.create(superConstructor.prototype, {
133 constructor: {
134 configurable: true,
135 enumerable: false,
136 value: constructor,
137 writable: true,
138 },
139 }), propertiesObject || {});
140 }
141 /**
142 * Returns true if `obj` is an object literal.
143 */
144 function isPlainObject(obj) {
145 if (typeof obj !== "object" || obj === null) {
146 return false;
147 }
148 return Object.getPrototypeOf(obj) === Object.prototype;
149 }
150 /**
151 * Returns the object's class name.
152 */
153 function getType(obj) {
154 return Object.prototype.toString.call(obj).replace(/^\[object (.+)]$/, "$1");
155 }
156 /**
157 * Returns a RFC4122 version 4 compilant UUID.
158 *
159 * @see http://stackoverflow.com/a/2117523
160 */
161 function getUuid() {
162 return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
163 const r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
164 return v.toString(16);
165 });
166 }
167 /**
168 * Recursively serializes an object into an encoded URI parameter string.
169 */
170 function serialize(obj, prefix) {
171 if (obj === null) {
172 return "";
173 }
174 const parameters = [];
175 Object.keys(obj).forEach((key) => {
176 const parameterKey = prefix ? prefix + "[" + key + "]" : key;
177 const value = obj[key];
178 if (typeof value === "object") {
179 parameters.push(serialize(value, parameterKey));
180 }
181 else {
182 parameters.push(encodeURIComponent(parameterKey) + "=" + encodeURIComponent(value));
183 }
184 });
185 return parameters.join("&");
186 }
187 /**
188 * Triggers a custom or built-in event.
189 */
190 function triggerEvent(element, eventName) {
191 if (eventName === "click" && element instanceof HTMLElement) {
192 element.click();
193 return;
194 }
195 const event = new Event(eventName, {
196 bubbles: true,
197 cancelable: true,
198 });
199 element.dispatchEvent(event);
200 }
201 /**
202 * Returns the unique prefix for the localStorage.
203 */
204 function getStoragePrefix() {
205 return _prefix;
206 }
207 /**
208 * Interprets a string value as a boolean value similar to the behavior of the
209 * legacy functions `elAttrBool()` and `elDataBool()`.
210 */
211 function stringToBool(value) {
212 return value === "1" || value === "true";
213 }
214 /**
215 * A function that emits a side effect and does not return anything.
216 *
217 * @see https://github.com/chodorowicz/ts-debounce/blob/62f30f2c3379b7b5e778fb1793e1fbfa17354894/src/index.ts
218 */
219 function debounce(func, waitMilliseconds = 50, options = {
220 isImmediate: false,
221 }) {
222 let timeoutId;
223 return function (...args) {
224 const doLater = () => {
225 timeoutId = undefined;
226 if (!options.isImmediate) {
227 func.apply(this, args);
228 }
229 };
230 const shouldCallNow = options.isImmediate && timeoutId === undefined;
231 if (timeoutId !== undefined) {
232 clearTimeout(timeoutId);
233 }
234 timeoutId = setTimeout(doLater, waitMilliseconds);
235 if (shouldCallNow) {
236 func.apply(this, args);
237 }
238 };
239 }
240 /**
241 * @deprecated 6.0
242 */
243 function enableLegacyInheritance(legacyClass) {
244 // This MUST NOT be an error to prevent bricking installations during the upgrade.
245 console.error("Relying on the legacy inheritance is no longer supported. Please migrate your code to use ES6 classes and inheritance.", legacyClass);
246 }
247 function getXsrfToken() {
248 const cookies = document.cookie.split(";").map((c) => c.trim());
249 const xsrfToken = cookies.find((c) => c.startsWith("XSRF-TOKEN="));
250 if (xsrfToken === undefined) {
251 return "COOKIE_NOT_FOUND";
252 }
253 const [_key, value] = xsrfToken.split(/=/, 2);
254 return decodeURIComponent(value.trim());
255 }
256 });