UNPKG

@kwaeri/developer-tools

Version:

The kwaeri developer tools. A minimalist tooling for kwaeri application development.

259 lines 11 kB
/** * SPDX-PackageName: kwaeri/developer-tools * SPDX-PackageVersion: 0.10.1 * SPDX-FileCopyrightText: © 2014 - 2022 Richard Winters <kirvedx@gmail.com> and contributors * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR MIT */ 'use strict'; /** * Class for implementing the Kwaeri Developer Tools (kdt) */ export class kdt { /** * A list of types for easing the implementation of our .type() method * * @var { object } */ classmap = { "[object Boolean]": "boolean", "[object Number]": "number", "[object String]": "string", "[object Function]": "function", "[object Array]": "array", "[object Date]": "date", "[object RegExp]": "regexp", "[object Object]": "object", }; /** * Class constructor */ constructor() { } /** * Checks the type of any entity * * @param { any } query The query to be type checked * * @returns { string } String The lowercase short variant of the type the entity evaluated to. */ type(...args) { // The working code below yields faster performance than the following snippet by almost 1.5 - 2 times the amount // ( http://stackoverflow.com/a/12022491 ): // // return ( o === null ) ? String( o ) : _type[ toString.call( o ) ] || "object"; if (arguments[0] === null) return String(arguments[0]); else return this.classmap[toString.call(arguments[0])] || "unknown"; } /** * Checks if an object is empty or not * * @param { object } The object which we check is empty * * @return { boolean } true if empty, false otherwise */ empty(...args) { return !Object.keys(arguments[0]).length && arguments[0].constructor === Object; } /** * An extend method that mimics jQuery and it's deep copy trait * * @param { object } a * @param { object } b * @param { object } x * * @return { object } a extended by b through x */ extend(...args) { let returnable = arguments[0]; for (let i = 1; i < arguments.length; i++) { for (let key in arguments[i]) { if (arguments[i].hasOwnProperty(key)) { if (returnable.hasOwnProperty(key) && typeof returnable[key] === 'object' && typeof arguments[i][key] === 'object') returnable[key] = this.extend(returnable[key], arguments[i][key]); else if (!(returnable.hasOwnProperty(key))) returnable[key] = arguments[i][key]; } } } return returnable; } /** * Method to apply mix-in [partial] classes to a base class. Based entirely on the official documentation * for the 'Alternate Version' - as described at: * https://www.typescriptlang.org/docs/handbook/mixins.html#alternative-pattern * * @param { any } derivedCtor The derived class being extended. Must be the named interface of the class. * @param { any } constructors The base classes to inherit from. Must be the named interfaces of the classes. * * @returns { void } */ compose(derivedCtor, constructors) { constructors.forEach((baseCtor) => { Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => { Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name)); }); }); } ; /** * Method for iterating over an object or list and performing an action for each index * * @param { object|array } iteratee The object or array being iterated over * @param { function } applicator The action to apply to each index of the iteratee, can be suppieda key/value arguments * * @return { boolean } Returns true if all went well, otherwise false */ each(iteratee, applicator) { // Ensure the second argument is a function if (this.type(applicator) === "function") { // Get the number of arguments expected const argumentCount = applicator.length; // Determine the type of the supplied object switch (this.type(iteratee)) { case "htmlcollection": case "nodelist": case "array": { // Iterate over the array for (let i = 0; i < iteratee.length; i++) { // Provide the index as the key to the provided function if (argumentCount > 1) { // Send the key and value applicator(i, iteratee[i]); } else if (argumentCount === 1) { // Send only the value applicator(iteratee[i]); } else { applicator(); } } } break; case "object": { // Iterate over the objects properties for (let property in iteratee) { // Supply the property name as the key to the provided function if (argumentCount > 1) applicator(property, iteratee[property]); // Send the key and value else if (argumentCount === 1) applicator(iteratee[property]); // Send only the value else applicator(); } } break; default: // First argument invalid, must be an Object or an associative Array console.log('Something went wrong with .each()', 'background: #000; color: #027;'); } // Everything went well return true; } // Second argument invalid, must be a function that takes 2 arguments return false; } /** * A specialized method for checking if an entity/variable is a number * * @param { any } entity * * @return { boolean } Returns true if the entity is a number, otherwise false */ isNumber(entity) { return !isNaN(parseFloat(entity)) && isFinite(entity); } /** * Helper method to determine if a property [chain] exists in an object * * @param { object } container The object for - and in - which the property chain is being checked. * @param { string } propertyChain The property chain being tested for. Nested properties can be notated with periods (x.y) * @returns True if the property chain exists, false if it does not. */ has(container, propertyChain) { return propertyChain.split(".").every((property) => { if (this.type(container) !== 'object' || container == null || !(property in container)) return false; container = container[property]; return true; }); } /** * Gets the specified value from the provided object if it exists, or returns the default value (or null) * * @param { Object } base The object a value is requested from * @param { string } property The name of the property that should belong to the object a value is requested from * @param { any } defaultValue A defaultvalue to return in the event either the provided object or requested property does not exist * * @returns { any } */ get(base = {}, property, defaultValue = null) { if (!this.empty(base)) { if (this.type(property) === 'string' || this.type(property) === 'number') { if (base.hasOwnProperty(property)) return base[property]; else { // Check if property string indicates a nested property: const level = property.split("."); // Do we need to be careful? let returnable = base; // Navigate the provided object until we've reached the // level of recursion indicated by the provided property // string: for (let i = 0; i < level.length; i++) { // If we reach an end prematurely, return the default value // (or null, by default): if (this.type(returnable) !== "object" || returnable == null || !(level[i] in returnable)) return defaultValue; returnable = returnable[level[i]]; } // Return the fetched property return returnable; } } } else return defaultValue; } /** * Sets the specified value to the specified property on the provided object * * @param { Object|Array<any> } base The provided object for which the specified value is to be set for the specified property * @param { string|number }property The property of the provided object for which to set the specified value * @param { any } value The value to set for the specified property of the provided object * * @returns { any } */ set(base = {}, property = "", value = null) { // Let's always return a new object: let returnable = base; if ((this.type(property) === 'string' && property !== "") || (this.type(property) === 'number' && property >= 0)) { switch (this.type(base)) { case 'object': { // Just in case, let's allow set to match get's allowable // property semantics: const level = property.split("."); // Whether caller did pass a nested property indicator or not, // for each level of recursion: for (let i = 0; i < level.length; i++) { // If it's the last, set the value to the property, // otherwise, just create an object to facilitate the recursion: returnable[level[i]] = (i === (level.length - 1)) ? value : {}; } } break; case 'array': { if (this.type(property) === 'number') returnable[property] = value; } break; } } return returnable; } } //# sourceMappingURL=kdt.mjs.map