ui-modules/utils/utils/general.js (80 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import angular from 'angular';
const MODULE_NAME = 'br.utils.general';
angular.module(MODULE_NAME, [])
.factory("brUtilsGeneral", brUtilsGeneralFactory)
.filter('capitalize', capitalizeFilter);
export default MODULE_NAME;
/* capitalizes the first word of a sentence or phrase */
export function capitalize(input) {
return (!!input) ? input.charAt(0).toUpperCase() + input.substr(1).toLowerCase() : '';
}
/* returns true if object is non-empty non-blank non-zero;
* understands arrays and plain objects (keys), numbers, and booleans,
* and anything with a length (string). also understands null and undefined.
*
* useful instead of the refrain of (x!==null && x.length>0), or a bit worse for Object.
*/
export function isNonEmpty(object) {
if (typeof object === "undefined" || object == null) return false;
// treat arrays specially although I think the two methods below will always work for them
if (Array.isArray(object)) return object.length > 0;
if (angular.isObject(object)) return Object.keys(object).length > 0;
// other common falsey types
if (object == 0 || object == false) return false;
// strings, maybe other things
if (object.hasOwnProperty("length")) return object.length > 0;
// other objects will be complex or default to true
return true;
}
export function uiModuleComparator(moduleA, moduleB) {
if(moduleA.order && moduleB.order){
if (moduleA.order != moduleB.order){
return moduleA.order - moduleB.order;
}
}
// If no order implemented or is the same, order by name
return moduleA.name.localeCompare(moduleB.name);
}
export function brUtilsGeneralFactory() {
return {
isNonEmpty,
capitalize,
uiModuleComparator
};
}
export function capitalizeFilter() {
return function(input) {
return capitalize(input);
}
}
const TOLERANCE = 0.0000000001;
export function isEqualWithinTolerance(n, n2, tolerance) {
return Math.abs(n2 - n)<(tolerance||TOLERANCE);
}
export function isInteger(n) {
return isEqualWithinTolerance(n, Math.round(n));
}
/** returns number rounded as first arg, and actual number of decimal places populated as second */
function roundNumericWithPlaces(n, maxDecimalDigits, minDecimalDigits, onlyCountSignificantDecimalDigits, countNines) {
maxDecimalDigits = maxDecimalDigits || 0;
minDecimalDigits = minDecimalDigits || 0;
if (countNines) n = 1-n;
// one recommended way to round; but seems inefficient using strings, causes round(0.499, 2) to show 0.5 not 0.50, and doesn't deal with significant decimal digits
// return Number(Math.round(Number(''+n+'e'+maxDecimalDigits))+'e-'+maxDecimalDigits);
let placesToShow = 0;
let significantPlaces = 0;
for (;;) {
if (isInteger(n)) break;
n *= 10;
if (onlyCountSignificantDecimalDigits && !significantPlaces) {
if (isEqualWithinTolerance(n, 0, 1)) {
// accept an extra digit if we are still dealing with insignificant digits
significantPlaces--;
}
}
significantPlaces++;
placesToShow++;
if (significantPlaces >= maxDecimalDigits && significantPlaces>0 && placesToShow>=minDecimalDigits) break;
}
let nr = Math.round(n);
if (!maxDecimalDigits && placesToShow > significantPlaces) {
// if no decimal digits but significant places then keep the right number of zeroes/ones
nr = Math.round(nr/10);
placesToShow--;
}
let i = placesToShow;
while (i-->0) nr/=10;
if (countNines) nr = 1-nr;
return [nr, Math.max(placesToShow, minDecimalDigits)];
}
/** rounds up to a given number of places after the decimal point;
* but unlike Number.toFixed if the number is exact, it does not create needless trailing zeros.
* so eg round(0.501, 2) will give 0.50 but round(0.50, 2) will give 0.5.
*
* optionally only counts significant digits, which ignores leading zeroes, so eg
* whereas round(0.00123, 2) would give 0.00, round(0.00123, 2, true) would give 0.0012.
* (this is especially useful when rounding nines).
*/
export function round(n, maxDecimalDigits, onlyCountSignificantDecimalDigits) {
const [number, places] = roundNumericWithPlaces(n, maxDecimalDigits, 0, onlyCountSignificantDecimalDigits, false);
return number;
}
/** as round, but returning a string */
export function rounded(n, maxDecimalDigits, onlyCountSignificantDecimalDigits) {
const [number, places] = roundNumericWithPlaces(n, maxDecimalDigits, 0, onlyCountSignificantDecimalDigits, false);
return number.toFixed(places);
}