Add content selection before removing content
[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-2018 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 * @module WoltLabSuite/Core/Core
8 */
9 define([], function() {
10 "use strict";
11
12 var _clone = function(variable) {
13 if (typeof variable === 'object' && (Array.isArray(variable) || Core.isPlainObject(variable))) {
14 return _cloneObject(variable);
15 }
16
17 return variable;
18 };
19
20 var _cloneObject = function(obj) {
21 if (!obj) {
22 return null;
23 }
24
25 if (Array.isArray(obj)) {
26 return obj.slice();
27 }
28
29 var newObj = {};
30 for (var key in obj) {
31 if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') {
32 newObj[key] = _clone(obj[key]);
33 }
34 }
35
36 return newObj;
37 };
38
39 //noinspection JSUnresolvedVariable
40 var _prefix = 'wsc' + window.WCF_PATH.hashCode() + '-';
41
42 /**
43 * @exports WoltLabSuite/Core/Core
44 */
45 var Core = {
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
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) {
63 return url.replace(/^index\.php\/(.*?)\/\?/, function(match, controller) {
64 var parts = controller.split(/([A-Z][a-z0-9]+)/);
65 controller = '';
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();
71 }
72 }
73
74 return 'index.php?' + controller + '/&';
75 });
76 },
77
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 || {};
87 var newObj = this.clone(out);
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) {
95 if (objOwns(obj, key)) {
96 if (!Array.isArray(obj[key]) && typeof obj[key] === 'object') {
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 }
104 }
105 else {
106 newObj[key] = obj[key];
107 }
108 }
109 }
110 }
111
112 return newObj;
113 },
114
115 /**
116 * Inherits the prototype methods from one constructor to another
117 * constructor.
118 *
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 *
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,
155 writable: true
156 }
157 }), propertiesObject || {});
158 },
159
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) {
167 if (typeof obj !== 'object' || obj === null || obj.nodeType) {
168 return false;
169 }
170
171 return (Object.getPrototypeOf(obj) === Object.prototype);
172 },
173
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
184 /**
185 * Returns a RFC4122 version 4 compilant UUID.
186 *
187 * @see http://stackoverflow.com/a/2117523
188 * @return {string}
189 */
190 getUuid: function() {
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
197 /**
198 * Recursively serializes an object into an encoded URI parameter string.
199 *
200 * @param {object} obj target object
201 * @param {string=} prefix parameter prefix
202 * @return {string} encoded parameter string
203 */
204 serialize: function(obj, prefix) {
205 var parameters = [];
206
207 for (var key in obj) {
208 if (objOwns(obj, key)) {
209 var parameterKey = (prefix) ? prefix + '[' + key + ']' : key;
210 var value = obj[key];
211
212 if (typeof value === 'object') {
213 parameters.push(this.serialize(value, parameterKey));
214 }
215 else {
216 parameters.push(encodeURIComponent(parameterKey) + '=' + encodeURIComponent(value));
217 }
218 }
219 }
220
221 return parameters.join('&');
222 },
223
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 });
238 }
239 catch (e) {
240 event = document.createEvent('Event');
241 event.initEvent(eventName, true, true);
242 }
243
244 element.dispatchEvent(event);
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;
254 }
255 };
256
257 return Core;
258 });