Commit | Line | Data |
---|---|---|
4bbf6ff1 AE |
1 | /** |
2 | * Provides the basic core functionality. | |
3 | * | |
4 | * @author Alexander Ebert | |
c839bd49 | 5 | * @copyright 2001-2018 WoltLab GmbH |
4bbf6ff1 | 6 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
58d7e8f8 | 7 | * @module WoltLabSuite/Core/Core |
4bbf6ff1 | 8 | */ |
a3a09eff | 9 | define([], function() { |
565853e8 TD |
10 | "use strict"; |
11 | ||
b5a32d79 | 12 | var _clone = function(variable) { |
cb57f345 | 13 | if (typeof variable === 'object' && (Array.isArray(variable) || Core.isPlainObject(variable))) { |
b5a32d79 AE |
14 | return _cloneObject(variable); |
15 | } | |
16 | ||
17 | return variable; | |
18 | }; | |
19 | ||
b5a32d79 AE |
20 | var _cloneObject = function(obj) { |
21 | if (!obj) { | |
22 | return null; | |
23 | } | |
24 | ||
25 | if (Array.isArray(obj)) { | |
cb57f345 | 26 | return obj.slice(); |
b5a32d79 AE |
27 | } |
28 | ||
29 | var newObj = {}; | |
30 | for (var key in obj) { | |
0e69f27c | 31 | if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') { |
b5a32d79 AE |
32 | newObj[key] = _clone(obj[key]); |
33 | } | |
34 | } | |
35 | ||
36 | return newObj; | |
37 | }; | |
38 | ||
0e69f27c AE |
39 | //noinspection JSUnresolvedVariable |
40 | var _prefix = 'wsc' + window.WCF_PATH.hashCode() + '-'; | |
41 | ||
4bbf6ff1 | 42 | /** |
58d7e8f8 | 43 | * @exports WoltLabSuite/Core/Core |
4bbf6ff1 | 44 | */ |
bc8cd0ff | 45 | var Core = { |
b5a32d79 AE |
46 | /** |
47 | * Deep clones an object. | |
48 | * | |
49 | * @param {object} obj source object | |
50 | * @return {object} cloned object | |
51 | */ | |
52 | clone: function(obj) { | |
53 | return _clone(obj); | |
54 | }, | |
55 | ||
827a69f3 AE |
56 | /** |
57 | * Converts WCF 2.0-style URLs into the default URL layout. | |
58 | * | |
59 | * @param string url target url | |
60 | * @return rewritten url | |
61 | */ | |
62 | convertLegacyUrl: function(url) { | |
827a69f3 | 63 | return url.replace(/^index\.php\/(.*?)\/\?/, function(match, controller) { |
bd969ed4 | 64 | var parts = controller.split(/([A-Z][a-z0-9]+)/); |
f0115fd9 | 65 | controller = ''; |
bd969ed4 AE |
66 | for (var i = 0, length = parts.length; i < length; i++) { |
67 | var part = parts[i].trim(); | |
68 | if (part.length) { | |
69 | if (controller.length) controller += '-'; | |
70 | controller += part.toLowerCase(); | |
827a69f3 AE |
71 | } |
72 | } | |
73 | ||
bd969ed4 | 74 | return 'index.php?' + controller + '/&'; |
827a69f3 AE |
75 | }); |
76 | }, | |
77 | ||
4bbf6ff1 AE |
78 | /** |
79 | * Merges objects with the first argument. | |
80 | * | |
81 | * @param {object} out destination object | |
82 | * @param {...object} arguments variable number of objects to be merged into the destination object | |
83 | * @return {object} destination object with all provided objects merged into | |
84 | */ | |
85 | extend: function(out) { | |
86 | out = out || {}; | |
15a48bb3 | 87 | var newObj = this.clone(out); |
4bbf6ff1 AE |
88 | |
89 | for (var i = 1, length = arguments.length; i < length; i++) { | |
90 | var obj = arguments[i]; | |
91 | ||
92 | if (!obj) continue; | |
93 | ||
94 | for (var key in obj) { | |
f5336f4f | 95 | if (objOwns(obj, key)) { |
827a69f3 | 96 | if (!Array.isArray(obj[key]) && typeof obj[key] === 'object') { |
15a48bb3 AE |
97 | if (this.isPlainObject(obj[key])) { |
98 | // object literals have the prototype of Object which in return has no parent prototype | |
99 | newObj[key] = this.extend(out[key], obj[key]); | |
100 | } | |
101 | else { | |
102 | newObj[key] = obj[key]; | |
103 | } | |
4bbf6ff1 AE |
104 | } |
105 | else { | |
15a48bb3 | 106 | newObj[key] = obj[key]; |
4bbf6ff1 AE |
107 | } |
108 | } | |
109 | } | |
110 | } | |
111 | ||
15a48bb3 AE |
112 | return newObj; |
113 | }, | |
114 | ||
0de17700 MS |
115 | /** |
116 | * Inherits the prototype methods from one constructor to another | |
117 | * constructor. | |
118 | * | |
3696feff AE |
119 | * Usage: |
120 | * | |
121 | * function MyDerivedClass() {} | |
122 | * Core.inherit(MyDerivedClass, TheAwesomeBaseClass, { | |
123 | * // regular prototype for `MyDerivedClass` | |
124 | * | |
125 | * overwrittenMethodFromBaseClass: function(foo, bar) { | |
126 | * // do stuff | |
127 | * | |
128 | * // invoke parent | |
129 | * MyDerivedClass._super.prototype.overwrittenMethodFromBaseClass.call(this, foo, bar); | |
130 | * } | |
131 | * }); | |
132 | * | |
0de17700 MS |
133 | * @see https://github.com/nodejs/node/blob/7d14dd9b5e78faabb95d454a79faa513d0bbc2a5/lib/util.js#L697-L735 |
134 | * @param {function} constructor inheriting constructor function | |
135 | * @param {function} superConstructor inherited constructor function | |
136 | * @param {object=} propertiesObject additional prototype properties | |
137 | */ | |
138 | inherit: function(constructor, superConstructor, propertiesObject) { | |
139 | if (constructor === undefined || constructor === null) { | |
140 | throw new TypeError("The constructor must not be undefined or null."); | |
141 | } | |
142 | if (superConstructor === undefined || superConstructor === null) { | |
143 | throw new TypeError("The super constructor must not be undefined or null."); | |
144 | } | |
145 | if (superConstructor.prototype === undefined) { | |
146 | throw new TypeError("The super constructor must have a prototype."); | |
147 | } | |
148 | ||
149 | constructor._super = superConstructor; | |
150 | constructor.prototype = Core.extend(Object.create(superConstructor.prototype, { | |
151 | constructor: { | |
152 | configurable: true, | |
153 | enumerable: false, | |
154 | value: constructor, | |
3b083558 | 155 | writable: true |
0de17700 MS |
156 | } |
157 | }), propertiesObject || {}); | |
158 | }, | |
159 | ||
15a48bb3 AE |
160 | /** |
161 | * Returns true if `obj` is an object literal. | |
162 | * | |
163 | * @param {*} obj target object | |
164 | * @returns {boolean} true if target is an object literal | |
165 | */ | |
166 | isPlainObject: function(obj) { | |
87d33e2c | 167 | if (typeof obj !== 'object' || obj === null || obj.nodeType) { |
15a48bb3 AE |
168 | return false; |
169 | } | |
170 | ||
87d33e2c | 171 | return (Object.getPrototypeOf(obj) === Object.prototype); |
4bbf6ff1 AE |
172 | }, |
173 | ||
827a69f3 AE |
174 | /** |
175 | * Returns the object's class name. | |
176 | * | |
177 | * @param {object} obj target object | |
178 | * @return {string} object class name | |
179 | */ | |
180 | getType: function(obj) { | |
181 | return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1'); | |
182 | }, | |
183 | ||
600d580f TD |
184 | /** |
185 | * Returns a RFC4122 version 4 compilant UUID. | |
186 | * | |
187 | * @see http://stackoverflow.com/a/2117523 | |
188 | * @return {string} | |
189 | */ | |
34ec14c8 | 190 | getUuid: function() { |
600d580f TD |
191 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { |
192 | var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); | |
193 | return v.toString(16); | |
194 | }); | |
195 | }, | |
196 | ||
827a69f3 AE |
197 | /** |
198 | * Recursively serializes an object into an encoded URI parameter string. | |
199 | * | |
200 | * @param {object} obj target object | |
15a48bb3 | 201 | * @param {string=} prefix parameter prefix |
0e69f27c | 202 | * @return {string} encoded parameter string |
827a69f3 | 203 | */ |
15a48bb3 | 204 | serialize: function(obj, prefix) { |
827a69f3 AE |
205 | var parameters = []; |
206 | ||
207 | for (var key in obj) { | |
f5336f4f | 208 | if (objOwns(obj, key)) { |
15a48bb3 | 209 | var parameterKey = (prefix) ? prefix + '[' + key + ']' : key; |
827a69f3 AE |
210 | var value = obj[key]; |
211 | ||
15a48bb3 AE |
212 | if (typeof value === 'object') { |
213 | parameters.push(this.serialize(value, parameterKey)); | |
827a69f3 | 214 | } |
15a48bb3 AE |
215 | else { |
216 | parameters.push(encodeURIComponent(parameterKey) + '=' + encodeURIComponent(value)); | |
827a69f3 | 217 | } |
827a69f3 AE |
218 | } |
219 | } | |
220 | ||
221 | return parameters.join('&'); | |
222 | }, | |
223 | ||
cb57f345 AE |
224 | /** |
225 | * Triggers a custom or built-in event. | |
226 | * | |
227 | * @param {Element} element target element | |
228 | * @param {string} eventName event name | |
229 | */ | |
230 | triggerEvent: function(element, eventName) { | |
231 | var event; | |
232 | ||
233 | try { | |
234 | event = new Event(eventName, { | |
235 | bubbles: true, | |
236 | cancelable: true | |
237 | }); | |
4bbf6ff1 | 238 | } |
cb57f345 AE |
239 | catch (e) { |
240 | event = document.createEvent('Event'); | |
241 | event.initEvent(eventName, true, true); | |
4bbf6ff1 | 242 | } |
cb57f345 AE |
243 | |
244 | element.dispatchEvent(event); | |
0e69f27c AE |
245 | }, |
246 | ||
247 | /** | |
248 | * Returns the unique prefix for the localStorage. | |
249 | * | |
250 | * @return {string} prefix for the localStorage | |
251 | */ | |
252 | getStoragePrefix: function() { | |
253 | return _prefix; | |
4bbf6ff1 AE |
254 | } |
255 | }; | |
256 | ||
bc8cd0ff | 257 | return Core; |
4bbf6ff1 | 258 | }); |