UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

152 lines (131 loc) 3.75 kB
/** * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html * * For those who do not wish to adhere to the AGPLv3, a commercial license is available. * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. * For more information about purchasing a commercial license, please contact schukai GmbH. * * SPDX-License-Identifier: AGPL-3.0 */ import { getGlobal } from "../types/global.mjs"; import { isArray, isFunction, isObject, isPrimitive, isProxy, } from "../types/is.mjs"; import { typeOf } from "../types/typeof.mjs"; import { validateObject } from "../types/validate.mjs"; export { clone }; /** * 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. * * @param {*} obj object to be cloned * @return {*} * @license AGPLv3 * @since 1.0.0 * @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)) { const copy = []; for (let i = 0, len = obj.length; i < len; i++) { copy[i] = clone(obj[i]); } return copy; } if (isObject(obj)) { // Handle Date if (obj instanceof Date) { const copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle URL if (obj instanceof URL) { return new URL(obj.toString()); } /** Do not clone DOM nodes */ if (typeof Element !== "undefined" && obj instanceof Element) return obj; if (typeof Document !== "undefined" && obj instanceof Document) return obj; if ( typeof DocumentFragment !== "undefined" && obj instanceof DocumentFragment ) return obj; /** Do not clone global objects */ if (obj === getGlobal()) 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; if (isProxy(obj)) return obj; // Handle Proxy-Object return cloneObject(obj); } throw new Error("unable to clone obj! its type isn't supported."); } /** * * @param {object} obj * @return {object} * @private */ function cloneObject(obj) { validateObject(obj); const fkt = obj?.["constructor"]; /** Object has clone method */ if (typeOf(fkt) === "function") { const prototype = fkt?.prototype; if (typeof prototype === "object") { if ( prototype.hasOwnProperty("getClone") && typeOf(obj.getClone) === "function" ) { return obj.getClone(); } } } let copy = {}; if ( typeof obj.constructor === "function" && typeof obj.constructor.call === "function" ) { copy = new obj.constructor(); } for (const key in obj) { if (!obj.hasOwnProperty(key)) { continue; } if (isPrimitive(obj[key])) { copy[key] = obj[key]; continue; } copy[key] = clone(obj[key]); } return copy; }