'use strict';
/**
* @author schukai GmbH
*/
import {Monster} from '../namespace.js';
import {isObject, isFunction, isPrimitive, isArray} from '../types/is.js';
import {getGlobal} from '../types/global.js';
/**
* with this function, objects can be cloned.
* the entire object tree is run through.
*
* Proxy, Element, HTMLDocument and DocumentFragment instances are not cloned.
* Global objects such as windows are also not cloned,
*
* If an object has a method `getClone()`, this method is used to create the clone.
*
* you can call the method via the monster namespace `Monster.Util.clone()`.
*
* ```
* <script type="module">
* import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/util/clone.js';
* console.log(Monster.Util.clone({}))
* </script>
* ```
*
* Alternatively, you can also integrate this function individually.
*
* ```
* <script type="module">
* import {clone} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/util/clone.js';
* console.log(clone({}))
* </script>
* ```
*
*
* @param {*} obj object to be cloned
* @returns {*}
*
* @since 1.0.0
* @memberOf Monster/Util
* @copyright schukai GmbH
* @throws {Error} unable to clone obj! its type isn't supported.
*/
function clone(obj) {
// typeof null results in 'object'. https://2ality.com/2013/10/typeof-null.html
if (null === obj) {
return obj;
}
// Handle the two simple types, null and undefined
if (isPrimitive(obj)) {
return obj;
}
// Handle the two simple types, null and undefined
if (isFunction(obj)) {
return obj;
}
// Handle Array
if (isArray(obj)) {
let copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
if (isObject(obj)) {
// Handle Date
if (obj instanceof Date) {
let copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
/** Do not clone DOM nodes */
if (typeof Element !== 'undefined' && obj instanceof Element) return obj;
if (typeof HTMLDocument !== 'undefined' && obj instanceof HTMLDocument) return obj;
if (typeof DocumentFragment !== 'undefined' && obj instanceof DocumentFragment) return obj;
/** Do not clone global objects */
if (obj === getGlobal()) return obj;
if (typeof globalContext !== 'undefined' && obj === globalContext) return obj;
if (typeof window !== 'undefined' && obj === window) return obj;
if (typeof document !== 'undefined' && obj === document) return obj;
if (typeof navigator !== 'undefined' && obj === navigator) return obj;
if (typeof JSON !== 'undefined' && obj === JSON) return obj;
// Handle Proxy-Object
try {
// try/catch because possible: TypeError: Function has non-object prototype 'undefined' in instanceof check
if (obj instanceof Proxy) {
return obj;
}
} catch (e) {
}
return cloneObject(obj)
}
throw new Error("unable to clone obj! its type isn't supported.");
}
/**
*
* @param {object} obj
* @returns {object}
* @private
*/
function cloneObject(obj) {
var copy;
/** Object has clone method */
if (typeof obj.hasOwnProperty('getClone') && obj.getClone === 'function') {
return obj.getClone();
}
copy = {};
if (typeof obj.constructor === 'function' &&
typeof obj.constructor.call === 'function') {
copy = new obj.constructor();
}
for (let key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
if (Monster.Types.isPrimitive(obj[key])) {
copy[key] = obj[key];
continue;
}
copy[key] = clone(obj[key]);
}
return copy;
}
Monster.assignToNamespace('Monster.Util', clone);
export {Monster, clone}