wordwrapjs
Advanced tools
Comparing version
@@ -8,299 +8,14 @@ (function (global, factory) { | ||
/** | ||
* Isomorphic, functional type-checking for Javascript. | ||
* @module typical | ||
* @typicalname t | ||
* @example | ||
* const t = require('typical') | ||
* const allDefined = array.every(t.isDefined) | ||
* @module wordwrapjs | ||
*/ | ||
/** | ||
* Returns true if input is a number. It is a more reasonable alternative to `typeof n` which returns `number` for `NaN` and `Infinity`. | ||
* | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* > t.isNumber(0) | ||
* true | ||
* > t.isNumber(1) | ||
* true | ||
* > t.isNumber(1.1) | ||
* true | ||
* > t.isNumber(0xff) | ||
* true | ||
* > t.isNumber(0644) | ||
* true | ||
* > t.isNumber(6.2e5) | ||
* true | ||
* > t.isNumber(NaN) | ||
* false | ||
* > t.isNumber(Infinity) | ||
* false | ||
* Wordwrap options. | ||
* @typedef {Object} WordwrapOptions | ||
* @property {number} [width=30] - The max column width in characters. | ||
* @property {boolean} [break=false] - If true, words exceeding the specified `width` will be forcefully broken | ||
* @property {boolean} [noTrim=false] - By default, each line output is trimmed. If `noTrim` is set, no line-trimming occurs - all whitespace from the input text is left in. | ||
* @property {string} [eol='\n'] - The end of line character to use. Defaults to `\n`. | ||
*/ | ||
function isNumber (n) { | ||
return !isNaN(parseFloat(n)) && isFinite(n) | ||
} | ||
/** | ||
* A plain object is a simple object literal, it is not an instance of a class. Returns true if the input `typeof` is `object` and directly decends from `Object`. | ||
* | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* > t.isPlainObject({ something: 'one' }) | ||
* true | ||
* > t.isPlainObject(new Date()) | ||
* false | ||
* > t.isPlainObject([ 0, 1 ]) | ||
* false | ||
* > t.isPlainObject(/test/) | ||
* false | ||
* > t.isPlainObject(1) | ||
* false | ||
* > t.isPlainObject('one') | ||
* false | ||
* > t.isPlainObject(null) | ||
* false | ||
* > t.isPlainObject((function * () {})()) | ||
* false | ||
* > t.isPlainObject(function * () {}) | ||
* false | ||
*/ | ||
function isPlainObject (input) { | ||
return input !== null && typeof input === 'object' && input.constructor === Object | ||
} | ||
/** | ||
* An array-like value has all the properties of an array yet is not an array instance. An example is the `arguments` object. Returns `true`` if the input value is an object, not `null`` and has a `length` property set with a numeric value. | ||
* | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* function sum(x, y){ | ||
* console.log(t.isArrayLike(arguments)) | ||
* // prints `true` | ||
* } | ||
*/ | ||
function isArrayLike (input) { | ||
return isObject(input) && typeof input.length === 'number' | ||
} | ||
/** | ||
* Returns true if the typeof input is `'object'` but not null. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isObject (input) { | ||
return typeof input === 'object' && input !== null | ||
} | ||
/** | ||
* Returns true if the input value is defined. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isDefined (input) { | ||
return typeof input !== 'undefined' | ||
} | ||
/** | ||
* Returns true if the input value is undefined. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isUndefined (input) { | ||
return !isDefined(input) | ||
} | ||
/** | ||
* Returns true if the input value is null. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isNull (input) { | ||
return input === null | ||
} | ||
/** | ||
* Returns true if the input value is not one of `undefined`, `null`, or `NaN`. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isDefinedValue (input) { | ||
return isDefined(input) && !isNull(input) && !Number.isNaN(input) | ||
} | ||
/** | ||
* Returns true if the input value is an ES2015 `class`. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isClass (input) { | ||
if (typeof input === 'function') { | ||
return /^class /.test(Function.prototype.toString.call(input)) | ||
} else { | ||
return false | ||
} | ||
} | ||
/** | ||
* Returns true if the input is a string, number, symbol, boolean, null or undefined value. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isPrimitive (input) { | ||
if (input === null) return true | ||
switch (typeof input) { | ||
case 'string': | ||
case 'number': | ||
case 'symbol': | ||
case 'undefined': | ||
case 'boolean': | ||
return true | ||
default: | ||
return false | ||
} | ||
} | ||
/** | ||
* Returns true if the input is a Promise. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isPromise (input) { | ||
if (input) { | ||
const isPromise = isDefined(Promise) && input instanceof Promise; | ||
const isThenable = input.then && typeof input.then === 'function'; | ||
return !!(isPromise || isThenable) | ||
} else { | ||
return false | ||
} | ||
} | ||
/** | ||
* Returns true if the input is an iterable (`Map`, `Set`, `Array`, Generator etc.). | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* > t.isIterable('string') | ||
* true | ||
* > t.isIterable(new Map()) | ||
* true | ||
* > t.isIterable([]) | ||
* true | ||
* > t.isIterable((function * () {})()) | ||
* true | ||
* > t.isIterable(Promise.resolve()) | ||
* false | ||
* > t.isIterable(Promise) | ||
* false | ||
* > t.isIterable(true) | ||
* false | ||
* > t.isIterable({}) | ||
* false | ||
* > t.isIterable(0) | ||
* false | ||
* > t.isIterable(1.1) | ||
* false | ||
* > t.isIterable(NaN) | ||
* false | ||
* > t.isIterable(Infinity) | ||
* false | ||
* > t.isIterable(function () {}) | ||
* false | ||
* > t.isIterable(Date) | ||
* false | ||
* > t.isIterable() | ||
* false | ||
* > t.isIterable({ then: function () {} }) | ||
* false | ||
*/ | ||
function isIterable (input) { | ||
if (input === null || !isDefined(input)) { | ||
return false | ||
} else { | ||
return ( | ||
typeof input[Symbol.iterator] === 'function' || | ||
typeof input[Symbol.asyncIterator] === 'function' | ||
) | ||
} | ||
} | ||
/** | ||
* Returns true if the input value is a string. The equivalent of `typeof input === 'string'` for use in funcitonal contexts. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isString (input) { | ||
return typeof input === 'string' | ||
} | ||
/** | ||
* Returns true if the input value is a function. The equivalent of `typeof input === 'function'` for use in funcitonal contexts. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isFunction (input) { | ||
return typeof input === 'function' | ||
} | ||
var t = { | ||
isNumber, | ||
isPlainObject, | ||
isArrayLike, | ||
isObject, | ||
isDefined, | ||
isUndefined, | ||
isNull, | ||
isDefinedValue, | ||
isClass, | ||
isPrimitive, | ||
isPromise, | ||
isIterable, | ||
isString, | ||
isFunction | ||
}; | ||
/** | ||
* Isomorphic map-reduce function to flatten an array into the supplied array. | ||
* | ||
* @module reduce-flatten | ||
* @example | ||
* const flatten = require('reduce-flatten') | ||
*/ | ||
/** | ||
* @alias module:reduce-flatten | ||
* @example | ||
* > numbers = [ 1, 2, [ 3, 4 ], 5 ] | ||
* > numbers.reduce(flatten, []) | ||
* [ 1, 2, 3, 4, 5 ] | ||
*/ | ||
function flatten (arr, curr) { | ||
if (Array.isArray(curr)) { | ||
arr.push(...curr); | ||
} else { | ||
arr.push(curr); | ||
} | ||
return arr | ||
} | ||
/** | ||
* @module wordwrapjs | ||
*/ | ||
const re = { | ||
@@ -315,11 +30,14 @@ chunk: /[^\s-]+?-\b|\S+|\s+|\r\n?|\n/g, | ||
*/ | ||
class WordWrap { | ||
constructor (text, options = {}) { | ||
if (!t.isDefined(text)) text = ''; | ||
class Wordwrap { | ||
/** | ||
* @param {string} text - The input text to wrap. | ||
* @param {module:wordwrapjs~WordwrapOptions} [options] | ||
*/ | ||
constructor (text = '', options = {}) { | ||
this._lines = String(text).split(/\r\n|\n/g); | ||
this.options = Object.assign({ | ||
eol: '\n', | ||
width: 30 | ||
}, options); | ||
this.options = { | ||
eol: '\n', | ||
width: 30, | ||
...options | ||
}; | ||
} | ||
@@ -329,16 +47,13 @@ | ||
/* trim each line of the supplied text */ | ||
return this._lines.map(trimLine.bind(this)) | ||
return this._lines.map(trimLine, this) | ||
/* split each line into an array of chunks, else mark it empty */ | ||
.map(line => line.match(re.chunk) || [ '~~empty~~' ]) | ||
.map(line => line.match(re.chunk) || ['~~empty~~']) | ||
/* optionally, break each word on the line into pieces */ | ||
.map(lineWords => { | ||
if (this.options.break) { | ||
return lineWords.map(breakWord.bind(this)) | ||
} else { | ||
return lineWords | ||
} | ||
}) | ||
.map(lineWords => lineWords.reduce(flatten, [])) | ||
.map(lineWords => this.options.break | ||
? lineWords.map(breakWord, this) | ||
: lineWords | ||
) | ||
.map(lineWords => lineWords.flat()) | ||
@@ -349,3 +64,3 @@ /* transforming the line of words to one or more new lines wrapped to size */ | ||
.reduce((lines, word) => { | ||
let currentLine = lines[lines.length - 1]; | ||
const currentLine = lines[lines.length - 1]; | ||
if (replaceAnsi(word).length + replaceAnsi(currentLine).length > this.options.width) { | ||
@@ -357,8 +72,8 @@ lines.push(word); | ||
return lines | ||
}, [ '' ]) | ||
}, ['']) | ||
}) | ||
.reduce(flatten, []) | ||
.flat() | ||
/* trim the wrapped lines */ | ||
.map(trimLine.bind(this)) | ||
.map(trimLine, this) | ||
@@ -381,9 +96,4 @@ /* filter out empty lines */ | ||
/** | ||
* @param {string} - the input text to wrap | ||
* @param [options] {object} - optional configuration | ||
* @param [options.width] {number} - the max column width in characters (defaults to 30). | ||
* @param [options.break] {boolean} - if true, words exceeding the specified `width` will be forcefully broken | ||
* @param [options.noTrim] {boolean} - By default, each line output is trimmed. If `noTrim` is set, no line-trimming occurs - all whitespace from the input text is left in. | ||
* @param [options.eol] {string} - The end of line character to use. Defaults to `\n`. | ||
* @return {string} | ||
* @param {string} text - the input text to wrap | ||
* @param {module:wordwrapjs~WordwrapOptions} [options] | ||
*/ | ||
@@ -397,4 +107,4 @@ static wrap (text, options) { | ||
* Wraps the input text, returning an array of strings (lines). | ||
* @param {string} - input text | ||
* @param {object} - Accepts same options as constructor. | ||
* @param {string} text - input text | ||
* @param {module:wordwrapjs~WordwrapOptions} [options] | ||
*/ | ||
@@ -408,11 +118,8 @@ static lines (text, options) { | ||
* Returns true if the input text would be wrapped if passed into `.wrap()`. | ||
* @param {string} - input text | ||
* @param {string} text - input text | ||
* @return {boolean} | ||
*/ | ||
static isWrappable (text) { | ||
if (t.isDefined(text)) { | ||
text = String(text); | ||
var matches = text.match(re.chunk); | ||
return matches ? matches.length > 1 : false | ||
} | ||
static isWrappable (text = '') { | ||
const matches = String(text).match(re.chunk); | ||
return matches ? matches.length > 1 : false | ||
} | ||
@@ -422,3 +129,3 @@ | ||
* Splits the input text into an array of words and whitespace. | ||
* @param {string} - input text | ||
* @param {string} text - input text | ||
* @returns {string[]} | ||
@@ -439,3 +146,7 @@ */ | ||
/* break a word into several pieces */ | ||
/** | ||
* break a word into several pieces | ||
* @param {string} word | ||
* @private | ||
*/ | ||
function breakWord (word) { | ||
@@ -455,4 +166,4 @@ if (replaceAnsi(word).length > this.options.width) { | ||
return WordWrap; | ||
return Wordwrap; | ||
}))); |
{ | ||
"name": "wordwrapjs", | ||
"author": "Lloyd Brookes <75pound@gmail.com>", | ||
"version": "5.0.2", | ||
"version": "5.1.0", | ||
"description": "Word-wrapping for javascript.", | ||
"repository": "https://github.com/75lb/wordwrapjs.git", | ||
"repository": "https://github.com/75lb/wordwrapjs", | ||
"license": "MIT", | ||
"main": "index.mjs", | ||
"type": "module", | ||
"exports": { | ||
"import": "./index.js", | ||
"require": "./dist/index.cjs" | ||
}, | ||
"keywords": [ | ||
@@ -18,7 +22,8 @@ "word", | ||
"engines": { | ||
"node": ">=14" | ||
"node": ">=12.17" | ||
}, | ||
"scripts": { | ||
"test": "npm run dist && test-runner test.mjs", | ||
"docs": "jsdoc2md -c jsdoc.conf -t README.hbs index.mjs > README.md; echo", | ||
"test": "npm run dist && npm run test:ci", | ||
"test:ci": "test-runner test/test.?js", | ||
"docs": "jsdoc2md -t README.hbs index.js > README.md", | ||
"dist": "rollup -c" | ||
@@ -28,14 +33,17 @@ }, | ||
"jsdoc-to-markdown": "^7.0.1", | ||
"rollup": "^2.44.0", | ||
"rollup": "^2.56.2", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"test-runner": "~0.8.14" | ||
"test-runner": "^0.9.8" | ||
}, | ||
"dependencies": { | ||
"reduce-flatten": "^3.0.0", | ||
"typical": "^6.0.1" | ||
}, | ||
"files": [ | ||
"index.mjs", | ||
"index.js", | ||
"dist" | ||
] | ||
], | ||
"standard": { | ||
"ignore": [ | ||
"dist", | ||
"tmp" | ||
], | ||
"envs": [] | ||
} | ||
} |
100
README.md
@@ -5,3 +5,3 @@ [](https://www.npmjs.org/package/wordwrapjs) | ||
[](https://github.com/75lb/wordwrapjs/network/dependents?dependent_type=PACKAGE) | ||
[](https://travis-ci.org/75lb/wordwrapjs) | ||
[](https://github.com/75lb/wordwrapjs/actions/workflows/node.js.yml) | ||
[](https://github.com/feross/standard) | ||
@@ -11,8 +11,4 @@ | ||
Word wrapping, with a few features. | ||
Word wrapping for plain text. | ||
- force-break option | ||
- wraps hypenated words | ||
- multilingual - wraps any language that uses whitespace for word separation. | ||
## Synopsis | ||
@@ -23,7 +19,6 @@ | ||
```js | ||
> wordwrap = require('wordwrapjs') | ||
import wordwrap from 'wordwrapjs' | ||
> text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' | ||
> result = wordwrap.wrap(text, { width: 20 }) | ||
const text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' | ||
const result = wordwrap.wrap(text, { width: 20 }) | ||
``` | ||
@@ -43,6 +38,9 @@ | ||
By default, long words will not break. Unless you set the `break` option. | ||
```js | ||
Force long words to wrap by setting the `break` flag. | ||
``` | ||
Welcome to Node.js v16.6.2. | ||
> wrap = require('wordwrapjs') | ||
> url = 'https://github.com/75lb/wordwrapjs' | ||
> wrap.lines(url, { width: 18 }) | ||
@@ -68,3 +66,3 @@ [ 'https://github.com/75lb/wordwrapjs' ] | ||
```js | ||
import wordwrap from 'wordwrapjs/index.mjs' | ||
import wordwrap from 'wordwrapjs' | ||
``` | ||
@@ -88,32 +86,41 @@ | ||
* [wordwrapjs](#module_wordwrapjs) | ||
* [WordWrap](#exp_module_wordwrapjs--WordWrap) ⏏ | ||
* [.wrap(text, [options])](#module_wordwrapjs--WordWrap.wrap) ⇒ <code>string</code> | ||
* [.lines(text, options)](#module_wordwrapjs--WordWrap.lines) | ||
* [.isWrappable(text)](#module_wordwrapjs--WordWrap.isWrappable) ⇒ <code>boolean</code> | ||
* [.getChunks(text)](#module_wordwrapjs--WordWrap.getChunks) ⇒ <code>Array.<string></code> | ||
* [Wordwrap](#exp_module_wordwrapjs--Wordwrap) ⏏ | ||
* [new Wordwrap(text, [options])](#new_module_wordwrapjs--Wordwrap_new) | ||
* _static_ | ||
* [.wrap(text, [options])](#module_wordwrapjs--Wordwrap.wrap) | ||
* [.lines(text, [options])](#module_wordwrapjs--Wordwrap.lines) | ||
* [.isWrappable(text)](#module_wordwrapjs--Wordwrap.isWrappable) ⇒ <code>boolean</code> | ||
* [.getChunks(text)](#module_wordwrapjs--Wordwrap.getChunks) ⇒ <code>Array.<string></code> | ||
* _inner_ | ||
* [~WordwrapOptions](#module_wordwrapjs--Wordwrap..WordwrapOptions) : <code>Object</code> | ||
<a name="exp_module_wordwrapjs--WordWrap"></a> | ||
<a name="exp_module_wordwrapjs--Wordwrap"></a> | ||
### WordWrap ⏏ | ||
### Wordwrap ⏏ | ||
**Kind**: Exported class | ||
<a name="module_wordwrapjs--WordWrap.wrap"></a> | ||
<a name="new_module_wordwrapjs--Wordwrap_new"></a> | ||
#### WordWrap.wrap(text, [options]) ⇒ <code>string</code> | ||
**Kind**: static method of [<code>WordWrap</code>](#exp_module_wordwrapjs--WordWrap) | ||
#### new Wordwrap(text, [options]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| text | <code>string</code> | The input text to wrap. | | ||
| [options] | [<code>WordwrapOptions</code>](#module_wordwrapjs--Wordwrap..WordwrapOptions) | | | ||
<a name="module_wordwrapjs--Wordwrap.wrap"></a> | ||
#### Wordwrap.wrap(text, [options]) | ||
**Kind**: static method of [<code>Wordwrap</code>](#exp_module_wordwrapjs--Wordwrap) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| text | <code>string</code> | the input text to wrap | | ||
| [options] | <code>object</code> | optional configuration | | ||
| [options.width] | <code>number</code> | the max column width in characters (defaults to 30). | | ||
| [options.break] | <code>boolean</code> | if true, words exceeding the specified `width` will be forcefully broken | | ||
| [options.noTrim] | <code>boolean</code> | By default, each line output is trimmed. If `noTrim` is set, no line-trimming occurs - all whitespace from the input text is left in. | | ||
| [options.eol] | <code>string</code> | The end of line character to use. Defaults to `\n`. | | ||
| [options] | [<code>WordwrapOptions</code>](#module_wordwrapjs--Wordwrap..WordwrapOptions) | | | ||
<a name="module_wordwrapjs--WordWrap.lines"></a> | ||
<a name="module_wordwrapjs--Wordwrap.lines"></a> | ||
#### WordWrap.lines(text, options) | ||
#### Wordwrap.lines(text, [options]) | ||
Wraps the input text, returning an array of strings (lines). | ||
**Kind**: static method of [<code>WordWrap</code>](#exp_module_wordwrapjs--WordWrap) | ||
**Kind**: static method of [<code>Wordwrap</code>](#exp_module_wordwrapjs--Wordwrap) | ||
@@ -123,10 +130,10 @@ | Param | Type | Description | | ||
| text | <code>string</code> | input text | | ||
| options | <code>object</code> | Accepts same options as constructor. | | ||
| [options] | [<code>WordwrapOptions</code>](#module_wordwrapjs--Wordwrap..WordwrapOptions) | | | ||
<a name="module_wordwrapjs--WordWrap.isWrappable"></a> | ||
<a name="module_wordwrapjs--Wordwrap.isWrappable"></a> | ||
#### WordWrap.isWrappable(text) ⇒ <code>boolean</code> | ||
#### Wordwrap.isWrappable(text) ⇒ <code>boolean</code> | ||
Returns true if the input text would be wrapped if passed into `.wrap()`. | ||
**Kind**: static method of [<code>WordWrap</code>](#exp_module_wordwrapjs--WordWrap) | ||
**Kind**: static method of [<code>Wordwrap</code>](#exp_module_wordwrapjs--Wordwrap) | ||
@@ -137,8 +144,8 @@ | Param | Type | Description | | ||
<a name="module_wordwrapjs--WordWrap.getChunks"></a> | ||
<a name="module_wordwrapjs--Wordwrap.getChunks"></a> | ||
#### WordWrap.getChunks(text) ⇒ <code>Array.<string></code> | ||
#### Wordwrap.getChunks(text) ⇒ <code>Array.<string></code> | ||
Splits the input text into an array of words and whitespace. | ||
**Kind**: static method of [<code>WordWrap</code>](#exp_module_wordwrapjs--WordWrap) | ||
**Kind**: static method of [<code>Wordwrap</code>](#exp_module_wordwrapjs--Wordwrap) | ||
@@ -149,5 +156,20 @@ | Param | Type | Description | | ||
<a name="module_wordwrapjs--Wordwrap..WordwrapOptions"></a> | ||
#### Wordwrap~WordwrapOptions : <code>Object</code> | ||
Wordwrap options. | ||
**Kind**: inner typedef of [<code>Wordwrap</code>](#exp_module_wordwrapjs--Wordwrap) | ||
**Properties** | ||
| Name | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| [width] | <code>number</code> | <code>30</code> | The max column width in characters. | | ||
| [break] | <code>boolean</code> | <code>false</code> | If true, words exceeding the specified `width` will be forcefully broken | | ||
| [noTrim] | <code>boolean</code> | <code>false</code> | By default, each line output is trimmed. If `noTrim` is set, no line-trimming occurs - all whitespace from the input text is left in. | | ||
| [eol] | <code>string</code> | <code>"'\\n'"</code> | The end of line character to use. Defaults to `\n`. | | ||
* * * | ||
© 2015-21 Lloyd Brookes \<75pound@gmail.com\>. Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
0
-100%8
14.29%167
15.17%Yes
NaN26666
-23.92%527
-43.64%1
Infinity%- Removed
- Removed
- Removed
- Removed