Commit | Line | Data |
---|---|---|
c9c0b67c AE |
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> | |
c9c0b67c AE |
7 | */ |
8 | define(["require", "exports"], function (require, exports) { | |
9 | "use strict"; | |
10 | Object.defineProperty(exports, "__esModule", { value: true }); | |
054537bf AE |
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; | |
c9c0b67c | 25 | const _clone = function (variable) { |
6ab9b916 | 26 | if (typeof variable === "object" && (Array.isArray(variable) || isPlainObject(variable))) { |
c9c0b67c AE |
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 = {}; | |
6ab9b916 | 39 | Object.keys(obj).forEach((key) => (newObj[key] = _clone(obj[key]))); |
c9c0b67c AE |
40 | return newObj; |
41 | }; | |
6ab9b916 | 42 | const _prefix = "wsc" + window.WCF_PATH.hashCode() + "-"; |
c9c0b67c AE |
43 | /** |
44 | * Deep clones an object. | |
45 | */ | |
46 | function clone(obj) { | |
47 | return _clone(obj); | |
48 | } | |
c9c0b67c AE |
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]+)/); | |
6ab9b916 | 55 | controller = ""; |
c9c0b67c AE |
56 | for (let i = 0, length = parts.length; i < length; i++) { |
57 | const part = parts[i].trim(); | |
58 | if (part.length) { | |
665fa171 | 59 | if (controller.length) { |
6ab9b916 | 60 | controller += "-"; |
665fa171 | 61 | } |
c9c0b67c AE |
62 | controller += part.toLowerCase(); |
63 | } | |
64 | } | |
65 | return `index.php?${controller}/&`; | |
66 | }); | |
67 | } | |
c9c0b67c AE |
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); | |
592ea62f | 78 | for (let i = 0, length = args.length; i < length; i++) { |
c9c0b67c | 79 | const obj = args[i]; |
665fa171 | 80 | if (!obj) { |
c9c0b67c | 81 | continue; |
665fa171 AE |
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]); | |
c9c0b67c AE |
88 | } |
89 | else { | |
90 | newObj[key] = obj[key]; | |
91 | } | |
92 | } | |
665fa171 AE |
93 | else { |
94 | newObj[key] = obj[key]; | |
95 | } | |
96 | }); | |
c9c0b67c AE |
97 | } |
98 | return newObj; | |
99 | } | |
c9c0b67c AE |
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 | |
e2fdcc2c | 119 | * @deprecated 5.4 Use the native `class` and `extends` keywords instead. |
c9c0b67c AE |
120 | */ |
121 | function inherit(constructor, superConstructor, propertiesObject) { | |
122 | if (constructor === undefined || constructor === null) { | |
6ab9b916 | 123 | throw new TypeError("The constructor must not be undefined or null."); |
c9c0b67c AE |
124 | } |
125 | if (superConstructor === undefined || superConstructor === null) { | |
6ab9b916 | 126 | throw new TypeError("The super constructor must not be undefined or null."); |
c9c0b67c AE |
127 | } |
128 | if (superConstructor.prototype === undefined) { | |
6ab9b916 | 129 | throw new TypeError("The super constructor must have a prototype."); |
c9c0b67c AE |
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 | } | |
c9c0b67c AE |
141 | /** |
142 | * Returns true if `obj` is an object literal. | |
143 | */ | |
144 | function isPlainObject(obj) { | |
665fa171 | 145 | if (typeof obj !== "object" || obj === null) { |
c9c0b67c AE |
146 | return false; |
147 | } | |
6ab9b916 | 148 | return Object.getPrototypeOf(obj) === Object.prototype; |
c9c0b67c | 149 | } |
c9c0b67c AE |
150 | /** |
151 | * Returns the object's class name. | |
152 | */ | |
153 | function getType(obj) { | |
6ab9b916 | 154 | return Object.prototype.toString.call(obj).replace(/^\[object (.+)]$/, "$1"); |
c9c0b67c | 155 | } |
c9c0b67c AE |
156 | /** |
157 | * Returns a RFC4122 version 4 compilant UUID. | |
158 | * | |
159 | * @see http://stackoverflow.com/a/2117523 | |
160 | */ | |
161 | function getUuid() { | |
6ab9b916 TD |
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; | |
c9c0b67c AE |
164 | return v.toString(16); |
165 | }); | |
166 | } | |
c9c0b67c AE |
167 | /** |
168 | * Recursively serializes an object into an encoded URI parameter string. | |
169 | */ | |
170 | function serialize(obj, prefix) { | |
72700514 AE |
171 | if (obj === null) { |
172 | return ""; | |
173 | } | |
3e862cb8 | 174 | const parameters = []; |
665fa171 AE |
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)); | |
c9c0b67c | 180 | } |
665fa171 AE |
181 | else { |
182 | parameters.push(encodeURIComponent(parameterKey) + "=" + encodeURIComponent(value)); | |
183 | } | |
184 | }); | |
6ab9b916 | 185 | return parameters.join("&"); |
c9c0b67c | 186 | } |
c9c0b67c AE |
187 | /** |
188 | * Triggers a custom or built-in event. | |
189 | */ | |
190 | function triggerEvent(element, eventName) { | |
3bc7d8d7 AE |
191 | if (eventName === "click" && element instanceof HTMLElement) { |
192 | element.click(); | |
193 | return; | |
194 | } | |
f10d9af6 AE |
195 | const event = new Event(eventName, { |
196 | bubbles: true, | |
197 | cancelable: true, | |
198 | }); | |
c9c0b67c AE |
199 | element.dispatchEvent(event); |
200 | } | |
c9c0b67c AE |
201 | /** |
202 | * Returns the unique prefix for the localStorage. | |
203 | */ | |
204 | function getStoragePrefix() { | |
205 | return _prefix; | |
206 | } | |
378fa43c AE |
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) { | |
6ab9b916 | 212 | return value === "1" || value === "true"; |
378fa43c | 213 | } |
8f85c642 AE |
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) { | |
665fa171 | 224 | const doLater = () => { |
8f85c642 AE |
225 | timeoutId = undefined; |
226 | if (!options.isImmediate) { | |
665fa171 | 227 | func.apply(this, args); |
8f85c642 AE |
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) { | |
665fa171 | 236 | func.apply(this, args); |
8f85c642 AE |
237 | } |
238 | }; | |
239 | } | |
fd6a70e0 AE |
240 | /** |
241 | * @deprecated 6.0 | |
242 | */ | |
cc2639d3 | 243 | function enableLegacyInheritance(legacyClass) { |
fd6a70e0 AE |
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); | |
cc2639d3 | 246 | } |
46cd659c | 247 | function getXsrfToken() { |
9a8134d3 TD |
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()); | |
46cd659c | 255 | } |
c9c0b67c | 256 | }); |