UNPKG

ilib-common

Version:

Common utility functions for ilib. iLib is a cross-engine library of internationalization (i18n) classes written in pure JS

184 lines (183 loc) 14.4 kB
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.callAll=callAll;exports.deepCopy=deepCopy;exports.extend=extend;exports.extend2=extend2;exports.fromCodePoint=fromCodePoint;exports.hashCode=hashCode;exports.indexOf=indexOf;exports.isArray=isArray;exports.isDate=isDate;exports.isEmpty=isEmpty;exports.mapString=mapString;exports.merge=merge;exports.pad=pad;exports.shallowCopy=shallowCopy;exports.toCodePoint=toCodePoint;exports.toHexString=toHexString;var _log4jsApi=_interopRequireDefault(require("@log4js-node/log4js-api"));function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _typeof(o){"@babel/helpers - typeof";return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o},_typeof(o)}/* * JSUtils.js - Misc utilities to work around Javascript engine differences * * Copyright © 2013-2015, 2018, 2021-2022 JEDLSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. */var logger=_log4jsApi["default"].getLogger("ilib-common");/** * @module JSUtils *//** * Polyfill to test whether an object is an javascript array. * * @static * @param {*} object The object to test * @return {boolean} return true if the object is an array * and false otherwise */function isArray(object){if(typeof Array.isArray==="function")return Array.isArray(object);if(_typeof(object)==="object"){return Object.prototype.toString.call(object)==="[object Array]"}return false};/** * Perform a shallow copy of the source object to the target object. This only * copies the assignments of the source properties to the target properties, * but not recursively from there.<p> * * * @static * @param {Object} source the source object to copy properties from * @param {Object} target the target object to copy properties into */function shallowCopy(source,target){var prop=undefined;if(source&&target){// using Object.assign is about 1/3 faster on nodejs if(typeof Object.assign==="function"){return Object.assign(target,source)}// polyfill for(prop in source){if(prop!==undefined&&typeof source[prop]!=="undefined"){target[prop]=source[prop]}}}};/** * Perform a recursive deep copy from the "from" object to the "deep" object. * * @static * @param {Object} from the object to copy from * @param {Object} to the object to copy to * @return {Object} a reference to the the "to" object */function deepCopy(from,to){var prop;for(prop in from){if(prop){if(_typeof(from[prop])==="object"){to[prop]={};deepCopy(from[prop],to[prop])}else{to[prop]=from[prop]}}}return to};/** * Map a string to the given set of alternate characters. If the target set * does not contain a particular character in the input string, then that * character will be copied to the output unmapped. * * @static * @param {string} str a string to map to an alternate set of characters * @param {Array.<string>|Object} map a mapping to alternate characters * @return {string} the source string where each character is mapped to alternate characters */function mapString(str,map){var mapped="";if(map&&str){for(var i=0;i<str.length;i++){var c=str.charAt(i);// TODO use a char iterator? mapped+=map[c]||c}}else{mapped=str}return mapped};/** * Check if an object is a member of the given array. This is a polyfill for * Array.indexOf. If this javascript engine * support indexOf, it is used directly. Otherwise, this function implements it * itself. The idea is to make sure that you can use the quick indexOf if it is * available, but use a slower implementation in older engines as well. * * @static * @param {Array.<Object|string|number>} array array to search * @param {Object|string|number} obj object being sought. This should be of the same type as the * members of the array being searched. If not, this function will not return * any results. * @return {number} index of the object in the array, or -1 if it is not in the array. */function indexOf(array,obj){if(!array||!obj){return-1}if(typeof array.indexOf==="function"){return array.indexOf(obj)}else{// polyfill for(var i=0;i<array.length;i++){if(array[i]===obj){return i}}return-1}};/** @private */var zeros="00000000000000000000000000000000";/** * Pad the str with zeros to the given length of digits. * * @static * @param {string|number} str the string or number to pad * @param {number} length the desired total length of the output string, padded * @param {boolean=} right if true, pad on the right side of the number rather than the left. * Default is false. */function pad(str,length,right){if(typeof str!=="string"){str=""+str}var start=0;// take care of negative numbers if(str.charAt(0)==="-"){start++}return str.length>=length+start?str:right?str+zeros.substring(0,length-str.length+start):str.substring(0,start)+zeros.substring(0,length-str.length+start)+str.substring(start)};/** * Convert a string into the hexadecimal representation * of the Unicode characters in that string. * * @static * @param {string} string The string to convert * @param {number=} limit the number of digits to use to represent the character (1 to 8) * @return {string} a hexadecimal representation of the * Unicode characters in the input string */function toHexString(string,limit){var i,result="",lim=limit&&limit<9?limit:4;if(!string){return""}for(i=0;i<string.length;i++){var ch=string.charCodeAt(i).toString(16);result+=pad(ch,lim)}return result.toUpperCase()};/** * Test whether an object in a Javascript Date. * * @static * @param {Object|null|undefined} object The object to test * @return {boolean} return true if the object is a Date * and false otherwise */function isDate(object){if(_typeof(object)==="object"){return Object.prototype.toString.call(object)==="[object Date]"}return false};/** * Merge the properties of object2 into object1 in a deep manner and return a merged * object. If the property exists in both objects, the value in object2 will overwrite * the value in object1. If a property exists in object1, but not in object2, its value * will not be touched. If a property exists in object2, but not in object1, it will be * added to the merged result.<p> * * Name1 and name2 are for creating debug output only. They are not necessary.<p> * * * @static * @param {*} object1 the object to merge into * @param {*} object2 the object to merge * @param {boolean=} replace if true, replace the array elements in object1 with those in object2. * If false, concatenate array elements in object1 with items in object2. * @param {string=} name1 name of the object being merged into * @param {string=} name2 name of the object being merged in * @return {Object} the merged object */function merge(object1,object2,replace,name1,name2){if(!object1&&object2){return object2}if(object1&&!object2){return object1}var prop=undefined,newObj={};for(prop in object1){if(prop&&typeof object1[prop]!=="undefined"){newObj[prop]=object1[prop]}}for(prop in object2){if(prop&&typeof object2[prop]!=="undefined"){if(isArray(object1[prop])&&isArray(object2[prop])){if(typeof replace!=="boolean"||!replace){newObj[prop]=[].concat(object1[prop]);newObj[prop]=newObj[prop].concat(object2[prop])}else{newObj[prop]=object2[prop]}}else if(_typeof(object1[prop])==="object"&&_typeof(object2[prop])==="object"){newObj[prop]=merge(object1[prop],object2[prop],replace)}else{// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily if(name1&&name2&&newObj[prop]==object2[prop]){logger.debug("Property "+prop+" in "+name1+" is being overridden by the same value in "+name2)}newObj[prop]=object2[prop]}}}return newObj};/** * Return true if the given object has no properties.<p> * * * @static * @param {Object} obj the object to check * @return {boolean} true if the given object has no properties, false otherwise */function isEmpty(obj){var prop=undefined;if(!obj){return true}for(prop in obj){if(prop&&typeof obj[prop]!=="undefined"){return false}}return true};/** * @static */function hashCode(obj){var hash=0;function addHash(hash,newValue){// co-prime numbers creates a nicely distributed hash hash*=65543;hash+=newValue;hash%=2147483647;return hash}function stringHash(str){var hash=0;for(var i=0;i<str.length;i++){hash=addHash(hash,str.charCodeAt(i))}return hash}switch(_typeof(obj)){case"undefined":hash=0;break;case"string":hash=stringHash(obj);break;case"function":case"number":case"xml":hash=stringHash(String(obj));break;case"boolean":hash=obj?1:0;break;case"object":var props=[];for(var p in obj){if(obj.hasOwnProperty(p)){props.push(p)}}// make sure the order of the properties doesn't matter props.sort();for(var i=0;i<props.length;i++){hash=addHash(hash,stringHash(props[i]));hash=addHash(hash,hashCode(obj[props[i]]))}break}return hash};/** * Calls the given action function on each element in the given * array arr asynchronously and in order and finally call the given callback when they are * all done. The action function should take the array to * process as its parameter, and a callback function. It should * process the first element in the array and then call its callback * function with the result of processing that element (if any). * * @param {Array.<Object>} arr the array to process * @param {Function(Array.<Object>, Function(*))} action the action * to perform on each element of the array * @param {Function(*)} callback the callback function to call * with the results of processing each element of the array. */function callAll(arr,action,callback,results){results=results||[];if(arr&&arr.length){action(arr,function(result){results.push(result);callAll(arr.slice(1),action,callback,results)})}else{callback(results)}};/** * Extend object1 by mixing in everything from object2 into it. The objects * are deeply extended, meaning that this method recursively descends the * tree in the objects and mixes them in at each level. Arrays are extended * by concatenating the elements of object2 onto those of object1. * * @static * @param {Object} object1 the target object to extend * @param {Object=} object2 the object to mix in to object1 * @return {Object} returns object1 */function extend(object1,object2){var prop=undefined;if(object2){for(prop in object2){// don't extend object with undefined or functions if(prop&&typeof object2[prop]!=="undefined"&&typeof object2[prop]!=="function"){if(isArray(object1[prop])&&isArray(object2[prop])){logger.trace("Merging array prop "+prop);object1[prop]=object1[prop].concat(object2[prop])}else if(_typeof(object1[prop])==="object"&&_typeof(object2[prop])==="object"){logger.trace("Merging object prop "+prop);if(prop!=="ilib"){object1[prop]=extend(object1[prop],object2[prop])}}else{logger.trace("Copying prop "+prop);// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily object1[prop]=object2[prop]}}}}return object1};function extend2(object1,object2){var prop=undefined;if(object2){for(prop in object2){// don't extend object with undefined or functions if(prop&&typeof object2[prop]!=="undefined"){if(isArray(object1[prop])&&isArray(object2[prop])){logger.trace("Merging array prop "+prop);object1[prop]=object1[prop].concat(object2[prop])}else if(_typeof(object1[prop])==="object"&&_typeof(object2[prop])==="object"){logger.trace("Merging object prop "+prop);if(prop!=="ilib"){object1[prop]=extend2(object1[prop],object2[prop])}}else{logger.trace("Copying prop "+prop);// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily object1[prop]=object2[prop]}}}}return object1};/** * Convert a UCS-4 code point to a Javascript string. The codepoint can be any valid * UCS-4 Unicode character, including supplementary characters. Standard Javascript * only supports supplementary characters using the UTF-16 encoding, which has * values in the range 0x0000-0xFFFF. String.fromCharCode() will only * give you a string containing 16-bit characters, and will not properly convert * the code point for a supplementary character (which has a value > 0xFFFF) into * two UTF-16 surrogate characters. Instead, it will just just give you whatever * single character happens to be the same as your code point modulo 0x10000, which * is almost never what you want.<p> * * Similarly, that means if you use String.charCodeAt() * you will only retrieve a 16-bit value, which may possibly be a single * surrogate character that is part of a surrogate pair representing a character * in the supplementary plane. It will not give you a code point. Use * IString.codePointAt() to access code points in a string, or use * an iterator to walk through the code points in a string. * * @static * @param {number} codepoint UCS-4 code point to convert to a character * @return {string} a string containing the character represented by the codepoint */function fromCodePoint(codepoint){if(codepoint<65536){return String.fromCharCode(codepoint)}else{var high=Math.floor(codepoint/65536)-1;var low=codepoint&65535;return String.fromCharCode(55296|(high&15)<<6|(low&64512)>>10)+String.fromCharCode(56320|low&1023)}};/** * Convert the character or the surrogate pair at the given * index into the intrinsic Javascript string to a Unicode * UCS-4 code point. * * @static * @param {string} str string to get the code point from * @param {number} index index into the string * @return {number} code point of the character at the * given index into the string */function toCodePoint(str,index){if(!str||str.length===0){return-1}var code=-1,high=str.charCodeAt(index);if(high>=55296&&high<=56319){if(str.length>index+1){var low=str.charCodeAt(index+1);if(low>=56320&&low<=57343){code=((high&960)>>6)+1<<16|((high&63)<<10|low&1023)}}}else{code=high}return code}; //# sourceMappingURL=JSUtils.js.map