reduce-precision 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -4
- package/package.json +6 -1
- package/ts/lib/bundle.min.js +1 -1
- package/ts/lib/src/format/index.d.ts +6 -0
- package/ts/lib/src/format/index.js +70 -4
- package/ts/lib/src/index.d.ts +2 -0
- package/ts/lib/src/index.js +3 -1
- package/ts/lib/src/symbols.d.ts +2 -0
- package/ts/lib/src/symbols.js +5 -0
- package/ts/lib/test/format/rial.spec.d.ts +1 -0
- package/ts/lib/test/format/rial.spec.js +38 -0
- package/ts/lib/test/format/symbol.spec.d.ts +1 -0
- package/ts/lib/test/format/symbol.spec.js +50 -0
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- Intelligent handling of very small and very large numbers
|
|
21
21
|
- Automatic thousand separators and decimal points based on the selected language
|
|
22
22
|
- TypeScript type definitions included
|
|
23
|
+
- Optional inline Toman SVG and structured parts for custom UI rendering (JavaScript/TypeScript)
|
|
23
24
|
|
|
24
25
|
## Installation
|
|
25
26
|
|
|
@@ -70,7 +71,7 @@ echo $formatter->toString(12345.678); // Default format
|
|
|
70
71
|
|
|
71
72
|
## Options
|
|
72
73
|
|
|
73
|
-
The `
|
|
74
|
+
The JavaScript/TypeScript `NumberFormatter` constructor accepts an optional `options` object with the following properties:
|
|
74
75
|
|
|
75
76
|
| Option | Type | Default | Description |
|
|
76
77
|
| --------------- | ---------------------------------------------------------- | ---------- | ----------------------------------------------------- |
|
|
@@ -82,6 +83,7 @@ The `format` function accepts an optional `options` object with the following pr
|
|
|
82
83
|
| `postfixMarker` | `string` | `'i'` | Postfix marker for HTML and Markdown output |
|
|
83
84
|
| `prefix` | `string` | `''` | Prefix string to be added before the formatted number |
|
|
84
85
|
| `postfix` | `string` | `''` | Postfix string to be added after the formatted number |
|
|
86
|
+
| `currencySymbol` | `'text'` \| `'svg'` | `'text'` | Toman symbol style for HTML output (JavaScript/TypeScript) |
|
|
85
87
|
|
|
86
88
|
## Examples
|
|
87
89
|
|
|
@@ -189,6 +191,88 @@ Formats the input number as an HTML string.
|
|
|
189
191
|
|
|
190
192
|
Formats the input number as a Markdown string.
|
|
191
193
|
|
|
194
|
+
## Toman SVG (TypeScript / JavaScript)
|
|
195
|
+
|
|
196
|
+
Opt in to the bundled icon for HTML output:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { NumberFormatter, tomanSymbolSvg } from 'reduce-precision';
|
|
200
|
+
|
|
201
|
+
const formatter = new NumberFormatter({
|
|
202
|
+
template: 'irt',
|
|
203
|
+
currencySymbol: 'svg', // default: 'text'
|
|
204
|
+
}).setLanguage('fa');
|
|
205
|
+
|
|
206
|
+
formatter.toHtmlString(12500); // localized amount with inline Toman SVG
|
|
207
|
+
formatter.toPlainString(12500); // existing text representation
|
|
208
|
+
const parts = formatter.formatToParts(12500);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Plain text, Markdown, other templates, and the existing JSON contract retain their
|
|
212
|
+
behavior. `toString()` follows the selected output format, as before. The icon uses
|
|
213
|
+
`currentColor`, a `1em` size, an accessible Toman label, and `.rp-currency-symbol` for styling.
|
|
214
|
+
SVG output escapes custom affix text. HTML markers support `i`, `b`, `em`,
|
|
215
|
+
`strong`, `span`, `small`, `sup`, and `sub`; other markers fall back to `span`
|
|
216
|
+
when SVG output is enabled.
|
|
217
|
+
|
|
218
|
+
`formatToParts()` returns `{ type, value }` objects. IRT parts separate `sign`,
|
|
219
|
+
`prefix`, `number`, `compact`, `currency`, `postfix`, and spacing (`literal`).
|
|
220
|
+
Render text parts as text nodes and replace the currency part with the exported
|
|
221
|
+
`tomanSymbolSvg` or your framework component. Compact parts explicitly separate
|
|
222
|
+
scale and currency (for example, `هزار میلیارد` and `ت` instead of `همت`), so joining
|
|
223
|
+
parts may differ from legacy plain output. Other templates currently return a
|
|
224
|
+
single `literal` part. Invalid/empty input returns an empty array.
|
|
225
|
+
|
|
226
|
+
This feature is currently available in the JavaScript/TypeScript implementation;
|
|
227
|
+
the PHP implementation is unchanged.
|
|
228
|
+
|
|
229
|
+
### Render parts in a browser
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
const output = document.querySelector('#price')!;
|
|
233
|
+
output.replaceChildren();
|
|
234
|
+
|
|
235
|
+
for (const part of formatter.formatToParts(12500)) {
|
|
236
|
+
if (part.type === 'currency') {
|
|
237
|
+
// Parse only the bundled SVG, never user-provided text.
|
|
238
|
+
const icon = new DOMParser()
|
|
239
|
+
.parseFromString(tomanSymbolSvg, 'image/svg+xml').documentElement;
|
|
240
|
+
output.appendChild(document.importNode(icon, true));
|
|
241
|
+
} else {
|
|
242
|
+
output.appendChild(document.createTextNode(part.value));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The exported `FormatPart` TypeScript type describes each part. Calling
|
|
248
|
+
`formatToParts()` does not change the formatter's selected output mode.
|
|
249
|
+
|
|
250
|
+
## Local demo
|
|
251
|
+
|
|
252
|
+
From a checkout of this repository:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
npm ci
|
|
256
|
+
npm run dev
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Open the local URL printed by Vite. Select **Toman**, choose **Text** or **SVG icon**,
|
|
260
|
+
and switch between **HTML**, **Plain**, **Markdown**, and **Parts preview**.
|
|
261
|
+
The demo displays the rendered result, raw output, and formatted object or parts.
|
|
262
|
+
Plain and Markdown remain textual even when SVG is selected.
|
|
263
|
+
|
|
264
|
+
Build the package and demo:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
npm run typecheck
|
|
268
|
+
npm test -- --runInBand
|
|
269
|
+
npm run build
|
|
270
|
+
npm run demo:build
|
|
271
|
+
npm run demo:preview
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The demo build is written to `demo-dist/` and is excluded from Git.
|
|
275
|
+
|
|
192
276
|
## Testing
|
|
193
277
|
|
|
194
278
|
### Node.js / TypeScript
|
|
@@ -200,7 +284,7 @@ You can run tests using Jest or any other preferred testing framework for TypeSc
|
|
|
200
284
|
You can run tests using PHPUnit:
|
|
201
285
|
|
|
202
286
|
```bash
|
|
203
|
-
./vendor/bin/phpunit tests
|
|
287
|
+
./vendor/bin/phpunit php/tests/NumberFormatterTest.php
|
|
204
288
|
```
|
|
205
289
|
|
|
206
290
|
## Contributing
|
|
@@ -210,5 +294,3 @@ Contributions are welcome! If you find a bug or have a feature request, please o
|
|
|
210
294
|
## License
|
|
211
295
|
|
|
212
296
|
This project is licensed under the [MIT License](LICENSE).
|
|
213
|
-
|
|
214
|
-
---
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reduce-precision",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./ts/lib/src/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"ts/lib/**/*"
|
|
8
8
|
],
|
|
9
9
|
"scripts": {
|
|
10
|
+
"dev": "vite --config demo/vite.config.mjs",
|
|
11
|
+
"prepack": "npm run clean && npm run build",
|
|
12
|
+
"demo:build": "vite build --config demo/vite.config.mjs",
|
|
13
|
+
"demo:preview": "vite preview --config demo/vite.config.mjs",
|
|
10
14
|
"build": "npm run build:tsc && npm run build:webpack",
|
|
11
15
|
"build:tsc": "tsc --project tsconfig.json",
|
|
12
16
|
"build:webpack": "webpack --config webpack.config.js",
|
|
@@ -49,6 +53,7 @@
|
|
|
49
53
|
"ts-loader": "^9.5.2",
|
|
50
54
|
"ts-node": "^10.9.2",
|
|
51
55
|
"typescript": "^5.9.2",
|
|
56
|
+
"vite": "^8.3.0",
|
|
52
57
|
"webpack": "^5.101.0",
|
|
53
58
|
"webpack-cli": "^6.0.1"
|
|
54
59
|
},
|
package/ts/lib/bundle.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ReducePrecision=t():e.ReducePrecision=t()}(self,()=>(()=>{"use strict";var e={900:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(e={}){this.languageBaseConfig={prefixMarker:"i",postfixMarker:"i",prefix:"",postfix:""},this.defaultLanguageConfig={en:Object.assign(Object.assign({},this.languageBaseConfig),{thousandSeparator:",",decimalSeparator:"."}),fa:Object.assign(Object.assign({},this.languageBaseConfig),{thousandSeparator:"٬",decimalSeparator:"٫"})},this.options=Object.assign({language:"en",template:"number",precision:"high",outputFormat:"plain"},this.defaultLanguageConfig.en),this.options=Object.assign(Object.assign({},this.options),e)}setLanguage(e,t={}){return this.options.language=e,this.options.prefixMarker=t.prefixMarker||this.defaultLanguageConfig[e].prefixMarker,this.options.postfixMarker=t.postfixMarker||this.defaultLanguageConfig[e].postfixMarker,this.options.prefix=t.prefix||this.defaultLanguageConfig[e].prefix,this.options.postfix=t.postfix||this.defaultLanguageConfig[e].postfix,this.options.thousandSeparator=t.thousandSeparator||this.defaultLanguageConfig[e].thousandSeparator,this.options.decimalSeparator=t.decimalSeparator||this.defaultLanguageConfig[e].decimalSeparator,this}setTemplate(e,t){return this.options.template=e,this.options.precision=t,this}toJson(e){const t=this.format(e);return delete t.value,t}toString(e){return this.format(e).value||""}toPlainString(e){return this.options.outputFormat="plain",this.format(e).value||""}toHtmlString(e){return this.options.outputFormat="html",this.format(e).value||""}toMdString(e){return this.options.outputFormat="markdown",this.format(e).value||""}isENotation(e){return/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)$/.test(e)}convertENotationToRegularNumber(e,t){let r=String(e);if(0===e&&t){const e=t.toLowerCase().split("e");if(2===e.length&&e[0].match(/^0\.0*$/))return e[0]}if(-1===r.toLowerCase().indexOf("e"))return r;if(Math.abs(e)<1&&0!==e){const t=r.toLowerCase().split("e");if(2===t.length){const r=parseInt(t[1],10);if(r<0){let i=Math.abs(r);return t[0].includes(".")&&(i+=t[0].split(".")[1].length),e.toFixed(Math.min(i,100))}}}const i=r.toLowerCase().split("e");if(2===i.length){const t=i[0],r=parseInt(i[1],10);let[n,o]=t.split(".");if(o=o||"",r>0){const e=n.startsWith("-")?"-":"";return e&&(n=n.substring(1)),o.length<=r?e+n+o.padEnd(r,"0"):e+n+o.substring(0,r)+"."+o.substring(r)}if(r<0){let i=Math.abs(r);return t.includes(".")&&(i+=t.split(".")[1].length),e.toFixed(Math.min(i,100))}return t}return r}format(e){var t;let{precision:r,template:i}=this.options;const{language:n,outputFormat:o,prefixMarker:s,postfixMarker:a,prefix:l,postfix:u,thousandSeparator:g,decimalSeparator:f}=this.options;if(null==e||""===e)return{};const p=e.toString();if("liveformat"===i){let e=this._sanitizeLiveInput(p);const r=g||",",i=f||".";let n=p.replace(/[٠-٩۰-۹]/g,function(e){return String(15&e.charCodeAt(0))});if("fa"===(null!==(t=this.options.language)&&void 0!==t?t:"en")){const e=this.defaultLanguageConfig.fa.decimalSeparator||"٫";n=n.replace(new RegExp(e,"g"),".")}if(this.isENotation(n)&&0===Number(n)){const t=p.toLowerCase().indexOf("e");if(t>-1){const r=p.substring(0,t).replace(/[٠-٩۰-۹]/g,function(e){return String(15&e.charCodeAt(0))});let n=r;r.includes(".")&&"."!==i?n=r.replace(".",i):r.includes("٫")&&"٫"!==i&&(n=r.replace("٫",i));const o=new RegExp(`^0\\${i}0*$`);n.match(o)&&(e=n)}}let o="";if(e.startsWith("-")&&(o="-",e=e.substring(1)),""===e)return{value:"",prefix:"",postfix:"",sign:"",wholeNumber:""};if("0"===e){let e="0";return"fa"===this.options.language&&(e=String.fromCharCode("0".charCodeAt(0)+1728)),{value:e,prefix:"",postfix:"",sign:"",wholeNumber:e}}e===i&&(e="0"+i);let s="",a="",l=!1;if(e.includes(i)){l=!0;const t=e.split(i);s=t[0],a=t.length>1?t[1]:""}else if("."!==i&&e.includes(".")){l=!0;const t=e.split(".");s=t[0],a=t.length>1?t[1]:""}else s=e;if(""===s)s="0";else if("0"!==s){const e=s.replace(/^0+/,"");s=""===e?"0":e}let u=s.replace(/\B(?=(\d{3})+(?!\d))/g,r);l&&(u+=i+a);const c=u;let h="",d=c;"-"===o&&(0!==parseFloat(c)||c.includes(".")?(h="-",d=o+c):h="");const m=this._convertToFarsiDigits(u);let b="";return"-"===o&&(b="0"===u?"":"-"),{value:b+m,prefix:"",postfix:"",sign:b,wholeNumber:m}}(null==i?void 0:i.match(/^(number|usd|irt|irr|percent|liveformat)$/g))||(i="number"),this.isENotation(p)&&(e=this.convertENotationToRegularNumber(Number(e)));let c=e.toString().replace(/[\u0660-\u0669\u06F0-\u06F9]/g,function(e){return String(15&e.charCodeAt(0))}).replace(/[^\d.-]/g,"");c=c.replace(/^0+(?=\d)/g,"");const h=Math.abs(Number(c));let d,m,b,x,v=0;if("auto"===r&&(i.match(/^(usd|irt|irr|number)$/g)?r=h>=1e-4&&h<1e11?"high":"medium":"percent"===i&&(r="low")),"medium"===r)if(h>=0&&h<1e-4)d=33,m=4,b=!1,x=!0;else if(h>=1e-4&&h<.001)d=7,m=4,b=!1,x=!1;else if(h>=.001&&h<.01)d=5,m=3,b=!1,x=!1;else if(h>=.001&&h<.1)d=3,m=2,b=!1,x=!1;else if(h>=.1&&h<1)d=1,m=1,b=!1,x=!1;else if(h>=1&&h<10)d=3,m=3,b=!1,x=!1;else if(h>=10&&h<100)d=2,m=2,b=!1,x=!1;else if(h>=100&&h<1e3)d=1,m=1,b=!1,x=!1;else if(h>=1e3){const e=Math.floor(Math.log10(h))%3;d=2-e,m=2-e,b=!0,x=!0}else d=0,m=0,b=!0,x=!0;else if("low"===r)if(h>=0&&h<.01)d=2,m=0,b=!0,x=!1,v=2;else if(h>=.01&&h<.1)d=2,m=1,b=!0,x=!1;else if(h>=.1&&h<1)d=2,m=2,b=!0,x=!1;else if(h>=1&&h<10)d=2,m=2,b=!0,x=!1,v=2;else if(h>=10&&h<100)d=1,m=1,b=!0,x=!1,v=1;else if(h>=100&&h<1e3)d=0,m=0,b=!0,x=!1;else if(h>=1e3){const e=Math.floor(Math.log10(h))%3;d=1-e,m=1-e,b=!0,x=!0}else d=0,m=0,b=!0,x=!0,v=2;else h>=0&&h<1?(d=33,m=4,b=!1,x=!1):h>=1&&h<10?(d=3,m=3,b=!0,x=!1):h>=10&&h<100||h>=100&&h<1e3?(d=2,m=2,b=!0,x=!1):h>=1e3&&h<1e4?(d=1,m=1,b=!0,x=!1):(d=0,m=0,b=!0,x=!1);return this.isENotation(p)&&(d=Math.max(d,20),b=!1),this.reducePrecision(c,d,m,b,x,v,i,n,o,s,a,l,u,g,f,p)}reducePrecision(e,t=30,r=4,i=!1,n=!1,o=0,s="number",a="en",l="plain",u="span",g="span",f="",p="",c=",",h=".",d=""){var m,b;if(null==e||""===e.trim())return{};"-0"!==e&&"-0.0"!==e||(e=e.substring(1)),e=e.toString();const x=s.match(/^(number|percent)$/g)?{"":"",K:" هزار",M:" میلیون",B:" میلیارد",T:" تریلیون",Qd:" کادریلیون",Qt:" کنتیلیون"}:{"":"",K:" هزار ت",M:" میلیون ت",B:" میلیارد ت",T:" همت",Qd:" هزار همت",Qt:" میلیون همت"},v=s.match(/^(number|percent)$/g)?{"":"",K:" هزار",M:" میلیون",B:" میلیارد",T:" تریلیون",Qd:" کادریلیون",Qt:" کنتیلیون"}:{"":"",K:" هزار تومان",M:" میلیون تومان",B:" میلیارد تومان",T:" هزار میلیارد تومان",Qd:" کادریلیون تومان",Qt:" کنتیلیون تومان"};let C=/^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(e);if(!C)return{};const S=C[1]||"";let M=C[2],$=C[3],N=C[4],w="",L="";if($.length>=30?($="0".padEnd(29,"0"),N="1"):$.length+r>t?(r=t-$.length)<1&&(r=1):M.length>21&&(M="0",$="",N=""),n&&M.length>=4){const e=Object.keys(x);let t=M,r=0;for(;+t>999&&r<e.length-1;)t=(+t/1e3).toFixed(2),r++;if(L=e[r],C=/^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(t.toString()),!C)return{};M=C[2],$=C[3],N=C[4]}N.length>r&&(i?parseInt(N[r])<5?N=N.substring(0,r):(N=(parseInt(N.substring(0,r))+1).toString(),N.length>r&&($.length>0?$=$.substring(0,$.length-1):(M=(Number(M)+1).toString(),N=N.substring(1)))):N=N.substring(0,r)),n&&""!==$&&""===L&&($="0"+$.length.toString().replace(/\d/g,function(e){return["₀","₁","₂","₃","₄","₅","₆","₇","₈","₉"][parseInt(e,10)]}));let E=`${$}${N}`;if(E.length>t&&!d.includes("e")&&(E=E.substring(0,t)),d.includes("e")||d.includes("E"));else if(d.includes(".")){const e=d.split(".");if(2===e.length){const t=e[1];if(t.length>E.length&&t.endsWith("0")){let e=0;for(let r=t.length-1;r>=0&&"0"===t[r];r--)e++;e>0&&(E=E.padEnd(E.length+e,"0"))}}}"usd"===s?(w="en"===a?"$":"",L||(L="fa"===a?" دلار":"")):"irr"===s?L||(L="fa"===a?" ر":" R"):"irt"===s?L||(L="fa"===a?" ت":" T"):"percent"===s&&(L+="en"===a?"%":L?" درصد":"٪"),w=f+w,L+=p,"html"===l?(w&&(w=`<${u}>${w}</${u}>`),L&&(L=`<${g}>${L}</${g}>`)):"markdown"===l&&(w&&(w=`${u}${w}${u}`),L&&(L=`${g}${L}${g}`));const Q=/\B(?=(\d{3})+(?!\d))/g,F=o?".".padEnd(o+1,"0"):"";let T,j="";T=t<=0||r<=0||""===N&&""===$?`${M.replace(Q,",")}${F}`:`${M.replace(Q,",")}.${E}`,j=`${S}${w}${T}${L}`;const k={value:j,prefix:w,postfix:L,sign:S,wholeNumber:T};return k.value=(null!==(m=null==k?void 0:k.value)&&void 0!==m?m:"").replace(/,/g,c).replace(/\./g,h),"fa"===a&&(k.value=(null!==(b=null==k?void 0:k.value)&&void 0!==b?b:"").replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(x[e])}),k.fullPostfix=L.replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(v[e])}),k.postfix=k.postfix.replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(x[e])}),k.wholeNumber=k.wholeNumber.replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(x[e])})),k}_sanitizeLiveInput(e){var t;let r=e;r=r.replace(/[٠-٩۰-۹]/g,function(e){return String(15&e.charCodeAt(0))});let i=r,n=null;if("fa"===(null!==(t=this.options.language)&&void 0!==t?t:"en")){const e=this.defaultLanguageConfig.fa.decimalSeparator||"٫";r.includes(e)&&(n=e,i=r.replace(new RegExp(e,"g"),"."))}if(this.isENotation(i)){const e=this.convertENotationToRegularNumber(Number(i),i);r=n&&e.includes(".")?e.replace(/\./g,n):e}return r}_convertToFarsiDigits(e){if("fa"!==this.options.language||null==e)return e;let t="";for(let r=0;r<e.length;r++){const i=e[r];t+=i>="0"&&i<="9"?String.fromCharCode(i.charCodeAt(0)+1728):i}return t}}},986:function(e,t,r){var i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.NumberFormatter=void 0;const n=i(r(900));t.NumberFormatter=n.default}},t={};return function r(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i].call(o.exports,o,o.exports,r),o.exports}(986)})());
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ReducePrecision=t():e.ReducePrecision=t()}(self,()=>(()=>{"use strict";var e={527:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.tomanSymbolSvg=void 0,t.tomanSymbolSvg='<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" class="rp-currency-symbol" role="img" aria-label="تومان" focusable="false" style="vertical-align:-0.15em" viewBox="0 0 24 24" fill="none">\n<g stroke="currentColor" stroke-width="1.65" stroke-linecap="round" stroke-linejoin="round">\n<path d="M3 6.3v1.3c0 2.1 1.25 3.3 3.5 3.3S10 9.7 10 7.6V6.3"/>\n<path d="M2.2 13.65v4.2c0 1.18.68 1.85 1.85 1.85H6.9"/>\n<path d="M9.1 19.7H7.05a2.05 2.05 0 1 1 2.05-2.05v2.05Z"/>\n<path d="M22 15.6v1.1c0 1.37-.82 2.2-2.25 2.2H16.5"/>\n<path d="M16.5 18.7v-1.45a2.1 2.1 0 0 0-4.2 0c0 1.27.8 2 2.1 2h2.1c-.5 1.8-2.55 2.8-5.35 2.8"/>\n</g>\n<g fill="currentColor">\n<circle cx="6.5" cy="3.55" r="0.9"/>\n<circle cx="18.85" cy="12.75" r="0.9"/>\n<circle cx="21.95" cy="12.75" r="0.9"/>\n</g>\n</svg>'},900:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=r(527);class i{constructor(e={}){this.languageBaseConfig={prefixMarker:"i",postfixMarker:"i",prefix:"",postfix:""},this.defaultLanguageConfig={en:Object.assign(Object.assign({},this.languageBaseConfig),{thousandSeparator:",",decimalSeparator:"."}),fa:Object.assign(Object.assign({},this.languageBaseConfig),{thousandSeparator:"٬",decimalSeparator:"٫"})},this.options=Object.assign({language:"en",template:"number",precision:"high",outputFormat:"plain"},this.defaultLanguageConfig.en),this.options=Object.assign(Object.assign({},this.options),e)}setLanguage(e,t={}){return this.options.language=e,this.options.prefixMarker=t.prefixMarker||this.defaultLanguageConfig[e].prefixMarker,this.options.postfixMarker=t.postfixMarker||this.defaultLanguageConfig[e].postfixMarker,this.options.prefix=t.prefix||this.defaultLanguageConfig[e].prefix,this.options.postfix=t.postfix||this.defaultLanguageConfig[e].postfix,this.options.thousandSeparator=t.thousandSeparator||this.defaultLanguageConfig[e].thousandSeparator,this.options.decimalSeparator=t.decimalSeparator||this.defaultLanguageConfig[e].decimalSeparator,this}setTemplate(e,t){return this.options.template=e,this.options.precision=t,this}toJson(e){const t=this.format(e,void 0,!0);return delete t.value,t}toString(e){return this.format(e).value||""}toPlainString(e){return this.options.outputFormat="plain",this.format(e).value||""}toHtmlString(e){return this.options.outputFormat="html",this.format(e).value||""}toMdString(e){return this.options.outputFormat="markdown",this.format(e).value||""}formatToParts(e){const t=[],r=new i(Object.assign(Object.assign({},this.options),{outputFormat:"plain"}));if(r.format(e,t),!t.length){const n=r.toPlainString(e);n&&t.push({type:"literal",value:n})}return t}isENotation(e){return/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)$/.test(e)}convertENotationToRegularNumber(e,t){let r=String(e);if(0===e&&t){const e=t.toLowerCase().split("e");if(2===e.length&&e[0].match(/^0\.0*$/))return e[0]}if(-1===r.toLowerCase().indexOf("e"))return r;if(Math.abs(e)<1&&0!==e){const t=r.toLowerCase().split("e");if(2===t.length){const r=parseInt(t[1],10);if(r<0){let n=Math.abs(r);return t[0].includes(".")&&(n+=t[0].split(".")[1].length),e.toFixed(Math.min(n,100))}}}const n=r.toLowerCase().split("e");if(2===n.length){const t=n[0],r=parseInt(n[1],10);let[i,a]=t.split(".");if(a=a||"",r>0){const e=i.startsWith("-")?"-":"";return e&&(i=i.substring(1)),a.length<=r?e+i+a.padEnd(r,"0"):e+i+a.substring(0,r)+"."+a.substring(r)}if(r<0){let n=Math.abs(r);return t.includes(".")&&(n+=t.split(".")[1].length),e.toFixed(Math.min(n,100))}return t}return r}format(e,t,r=!1){var n;let{precision:i,template:a}=this.options;const{language:o,outputFormat:s,prefixMarker:l,postfixMarker:u,prefix:c,postfix:g,thousandSeparator:p,decimalSeparator:f}=this.options;if(null==e||""===e)return{};const h=e.toString();if("liveformat"===a){let e=this._sanitizeLiveInput(h);const t=p||",",r=f||".";let i=h.replace(/[٠-٩۰-۹]/g,function(e){return String(15&e.charCodeAt(0))});if("fa"===(null!==(n=this.options.language)&&void 0!==n?n:"en")){const e=this.defaultLanguageConfig.fa.decimalSeparator||"٫";i=i.replace(new RegExp(e,"g"),".")}if(this.isENotation(i)&&0===Number(i)){const t=h.toLowerCase().indexOf("e");if(t>-1){const n=h.substring(0,t).replace(/[٠-٩۰-۹]/g,function(e){return String(15&e.charCodeAt(0))});let i=n;n.includes(".")&&"."!==r?i=n.replace(".",r):n.includes("٫")&&"٫"!==r&&(i=n.replace("٫",r));const a=new RegExp(`^0\\${r}0*$`);i.match(a)&&(e=i)}}let a="";if(e.startsWith("-")&&(a="-",e=e.substring(1)),""===e)return{value:"",prefix:"",postfix:"",sign:"",wholeNumber:""};if("0"===e){let e="0";return"fa"===this.options.language&&(e=String.fromCharCode("0".charCodeAt(0)+1728)),{value:e,prefix:"",postfix:"",sign:"",wholeNumber:e}}e===r&&(e="0"+r);let o="",s="",l=!1;if(e.includes(r)){l=!0;const t=e.split(r);o=t[0],s=t.length>1?t[1]:""}else if("."!==r&&e.includes(".")){l=!0;const t=e.split(".");o=t[0],s=t.length>1?t[1]:""}else o=e;if(""===o)o="0";else if("0"!==o){const e=o.replace(/^0+/,"");o=""===e?"0":e}let u=o.replace(/\B(?=(\d{3})+(?!\d))/g,t);l&&(u+=r+s);const c=u;let g="",d=c;"-"===a&&(0!==parseFloat(c)||c.includes(".")?(g="-",d=a+c):g="");const m=this._convertToFarsiDigits(u);let v="";return"-"===a&&(v="0"===u?"":"-"),{value:v+m,prefix:"",postfix:"",sign:v,wholeNumber:m}}(null==a?void 0:a.match(/^(number|usd|irt|irr|percent|liveformat)$/g))||(a="number"),this.isENotation(h)&&(e=this.convertENotationToRegularNumber(Number(e)));let d=e.toString().replace(/[\u0660-\u0669\u06F0-\u06F9]/g,function(e){return String(15&e.charCodeAt(0))}).replace(/[^\d.-]/g,"");d=d.replace(/^0+(?=\d)/g,"");const m=Math.abs(Number(d));let v,b,x,S,C=0;if("auto"===i&&(a.match(/^(usd|irt|irr|number)$/g)?i=m>=1e-4&&m<1e11?"high":"medium":"percent"===a&&(i="low")),"medium"===i)if(m>=0&&m<1e-4)v=33,b=4,x=!1,S=!0;else if(m>=1e-4&&m<.001)v=7,b=4,x=!1,S=!1;else if(m>=.001&&m<.01)v=5,b=3,x=!1,S=!1;else if(m>=.001&&m<.1)v=3,b=2,x=!1,S=!1;else if(m>=.1&&m<1)v=1,b=1,x=!1,S=!1;else if(m>=1&&m<10)v=3,b=3,x=!1,S=!1;else if(m>=10&&m<100)v=2,b=2,x=!1,S=!1;else if(m>=100&&m<1e3)v=1,b=1,x=!1,S=!1;else if(m>=1e3){const e=Math.floor(Math.log10(m))%3;v=2-e,b=2-e,x=!0,S=!0}else v=0,b=0,x=!0,S=!0;else if("low"===i)if(m>=0&&m<.01)v=2,b=0,x=!0,S=!1,C=2;else if(m>=.01&&m<.1)v=2,b=1,x=!0,S=!1;else if(m>=.1&&m<1)v=2,b=2,x=!0,S=!1;else if(m>=1&&m<10)v=2,b=2,x=!0,S=!1,C=2;else if(m>=10&&m<100)v=1,b=1,x=!0,S=!1,C=1;else if(m>=100&&m<1e3)v=0,b=0,x=!0,S=!1;else if(m>=1e3){const e=Math.floor(Math.log10(m))%3;v=1-e,b=1-e,x=!0,S=!0}else v=0,b=0,x=!0,S=!0,C=2;else m>=0&&m<1?(v=33,b=4,x=!1,S=!1):m>=1&&m<10?(v=3,b=3,x=!0,S=!1):m>=10&&m<100||m>=100&&m<1e3?(v=2,b=2,x=!0,S=!1):m>=1e3&&m<1e4?(v=1,b=1,x=!0,S=!1):(v=0,b=0,x=!0,S=!1);return this.isENotation(h)&&(v=Math.max(v,20),x=!1),this.reducePrecision(d,v,b,x,S,C,a,o,s,l,u,c,g,p,f,h,t,r)}reducePrecision(e,t=30,r=4,i=!1,a=!1,o=0,s="number",l="en",u="plain",c="span",g="span",p="",f="",h=",",d=".",m="",v,b=!1){var x,S;if(null==e||""===e.trim())return{};"-0"!==e&&"-0.0"!==e||(e=e.substring(1)),e=e.toString();const C=s.match(/^(number|percent)$/g)?{"":"",K:" هزار",M:" میلیون",B:" میلیارد",T:" تریلیون",Qd:" کادریلیون",Qt:" کنتیلیون"}:{"":"",K:" هزار ت",M:" میلیون ت",B:" میلیارد ت",T:" همت",Qd:" هزار همت",Qt:" میلیون همت"},M=s.match(/^(number|percent)$/g)?{"":"",K:" هزار",M:" میلیون",B:" میلیارد",T:" تریلیون",Qd:" کادریلیون",Qt:" کنتیلیون"}:{"":"",K:" هزار تومان",M:" میلیون تومان",B:" میلیارد تومان",T:" هزار میلیارد تومان",Qd:" کادریلیون تومان",Qt:" کنتیلیون تومان"};"irr"===s&&(Object.assign(C,{K:" هزار ر",M:" میلیون ر",B:" میلیارد ر",T:" هزار میلیارد ر",Qd:" کادریلیون ر",Qt:" کنتیلیون ر"}),Object.assign(M,{K:" هزار ریال",M:" میلیون ریال",B:" میلیارد ریال",T:" هزار میلیارد ریال",Qd:" کادریلیون ریال",Qt:" کنتیلیون ریال"}));let y=/^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(e);if(!y)return{};const $=y[1]||"";let w=y[2],Q=y[3],N=y[4],T="",j="";if(Q.length>=30?(Q="0".padEnd(29,"0"),N="1"):Q.length+r>t?(r=t-Q.length)<1&&(r=1):w.length>21&&(w="0",Q="",N=""),a&&w.length>=4){const e=Object.keys(C);let t=w,r=0;for(;+t>999&&r<e.length-1;)t=(+t/1e3).toFixed(2),r++;if(j=e[r],y=/^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(t.toString()),!y)return{};w=y[2],Q=y[3],N=y[4]}N.length>r&&(i?parseInt(N[r])<5?N=N.substring(0,r):(N=(parseInt(N.substring(0,r))+1).toString(),N.length>r&&(Q.length>0?Q=Q.substring(0,Q.length-1):(w=(Number(w)+1).toString(),N=N.substring(1)))):N=N.substring(0,r)),a&&""!==Q&&""===j&&(Q="0"+Q.length.toString().replace(/\d/g,function(e){return["₀","₁","₂","₃","₄","₅","₆","₇","₈","₉"][parseInt(e,10)]}));let B=`${Q}${N}`;if(B.length>t&&!m.includes("e")&&(B=B.substring(0,t)),m.includes("e")||m.includes("E"));else if(m.includes(".")){const e=m.split(".");if(2===e.length){const t=e[1];if(t.length>B.length&&t.endsWith("0")){let e=0;for(let r=t.length-1;r>=0&&"0"===t[r];r--)e++;e>0&&(B=B.padEnd(B.length+e,"0"))}}}const L=j;"usd"===s?(T="en"===l?"$":"",j||(j="fa"===l?" دلار":"")):"irr"===s?j||(j="fa"===l?" ر":" R"):"irt"===s?j||(j="fa"===l?" ت":" T"):"percent"===s&&(j+="en"===l?"%":j?" درصد":"٪"),T=p+T,j+=f,"html"===u?(T&&(T=`<${c}>${T}</${c}>`),j&&(j=`<${g}>${j}</${g}>`)):"markdown"===u&&(T&&(T=`${c}${T}${c}`),j&&(j=`${g}${j}${g}`));const O=/\B(?=(\d{3})+(?!\d))/g,k=o?".".padEnd(o+1,"0"):"";let E,F="";E=t<=0||r<=0||""===N&&""===Q?`${w.replace(O,",")}${k}`:`${w.replace(O,",")}.${B}`,F=`${$}${T}${E}${j}`;const _={value:F,prefix:T,postfix:j,sign:$,wholeNumber:E};if(_.value=(null!==(x=null==_?void 0:_.value)&&void 0!==x?x:"").replace(/,/g,h).replace(/\./g,d),"fa"===l&&(_.value=(null!==(S=null==_?void 0:_.value)&&void 0!==S?S:"").replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(C[e])}),_.fullPostfix=j.replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(M[e])}),_.postfix=_.postfix.replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(C[e])}),_.wholeNumber=_.wholeNumber.replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)).replace(/(K|M|B|T|Qt|Qd)/g,function(e){return String(C[e])})),"irt"===s&&(v||"html"===u&&"svg"===this.options.currencySymbol&&!b)){const e=e=>"fa"===l?e.replace(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)+1728)):e,t="fa"===l?{K:"هزار",M:"میلیون",B:"میلیارد",T:"هزار میلیارد",Qd:"کادریلیون",Qt:"کنتیلیون"}:{K:"K",M:"M",B:"B",T:"T",Qd:"Qd",Qt:"Qt"},r=[];if($&&r.push({type:"sign",value:$}),p&&r.push({type:"prefix",value:e(p)}),r.push({type:"number",value:e(E.replace(/,/g,h).replace(/\./g,d))}),r.push({type:"literal",value:" "}),L&&(r.push({type:"compact",value:t[L]||L}),r.push({type:"literal",value:" "})),r.push({type:"currency",value:"fa"===l?"ت":"T"}),f&&r.push({type:"postfix",value:e(f)}),v&&v.push(...r),"html"===u&&"svg"===this.options.currencySymbol&&!b){const t=e=>e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),i=(e,t)=>{const r=/^(?:i|b|em|strong|span|small|sup|sub)$/i.test(t)?t:"span";return e?`<${r}>${e}</${r}>`:""},a=r.findIndex(e=>"number"===e.type),o=r.slice(a+1).map(e=>"currency"===e.type?n.tomanSymbolSvg.replace('aria-label="تومان"',`aria-label="${"fa"===l?"تومان":"Toman"}"`):t(e.value)).join("");_.value=t($)+i(t(e(p)),c)+t(r[a].value)+i(o,g)}}else v&&_.value&&v.push({type:"literal",value:_.value});return _}_sanitizeLiveInput(e){var t;let r=e;r=r.replace(/[٠-٩۰-۹]/g,function(e){return String(15&e.charCodeAt(0))});let n=r,i=null;if("fa"===(null!==(t=this.options.language)&&void 0!==t?t:"en")){const e=this.defaultLanguageConfig.fa.decimalSeparator||"٫";r.includes(e)&&(i=e,n=r.replace(new RegExp(e,"g"),"."))}if(this.isENotation(n)){const e=this.convertENotationToRegularNumber(Number(n),n);r=i&&e.includes(".")?e.replace(/\./g,i):e}return r}_convertToFarsiDigits(e){if("fa"!==this.options.language||null==e)return e;let t="";for(let r=0;r<e.length;r++){const n=e[r];t+=n>="0"&&n<="9"?String.fromCharCode(n.charCodeAt(0)+1728):n}return t}}t.default=i},986:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.tomanSymbolSvg=t.NumberFormatter=void 0;const i=n(r(900));t.NumberFormatter=i.default;var a=r(527);Object.defineProperty(t,"tomanSymbolSvg",{enumerable:!0,get:function(){return a.tomanSymbolSvg}})}},t={};return function r(n){var i=t[n];if(void 0!==i)return i.exports;var a=t[n]={exports:{}};return e[n].call(a.exports,a,a.exports,r),a.exports}(986)})());
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export interface FormatPart {
|
|
2
|
+
type: 'sign' | 'prefix' | 'number' | 'compact' | 'currency' | 'postfix' | 'literal';
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
1
5
|
type Template = 'number' | 'usd' | 'irt' | 'irr' | 'percent' | 'liveformat';
|
|
2
6
|
type Precision = 'auto' | 'high' | 'medium' | 'low';
|
|
3
7
|
type Language = 'en' | 'fa';
|
|
@@ -23,6 +27,7 @@ interface Options extends LanguageConfig {
|
|
|
23
27
|
template?: Template;
|
|
24
28
|
language?: Language;
|
|
25
29
|
outputFormat?: OutputFormat;
|
|
30
|
+
currencySymbol?: 'text' | 'svg';
|
|
26
31
|
}
|
|
27
32
|
declare class NumberFormatter {
|
|
28
33
|
private readonly languageBaseConfig;
|
|
@@ -36,6 +41,7 @@ declare class NumberFormatter {
|
|
|
36
41
|
toPlainString(input: string | number): string;
|
|
37
42
|
toHtmlString(input: string | number): string;
|
|
38
43
|
toMdString(input: string | number): string;
|
|
44
|
+
formatToParts(input: string | number): FormatPart[];
|
|
39
45
|
private isENotation;
|
|
40
46
|
private convertENotationToRegularNumber;
|
|
41
47
|
private format;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const symbols_1 = require("../symbols");
|
|
3
4
|
class NumberFormatter {
|
|
4
5
|
constructor(options = {}) {
|
|
5
6
|
this.languageBaseConfig = {
|
|
@@ -39,7 +40,7 @@ class NumberFormatter {
|
|
|
39
40
|
return this;
|
|
40
41
|
}
|
|
41
42
|
toJson(input) {
|
|
42
|
-
const formattedObject = this.format(input);
|
|
43
|
+
const formattedObject = this.format(input, undefined, true);
|
|
43
44
|
delete formattedObject.value;
|
|
44
45
|
return formattedObject;
|
|
45
46
|
}
|
|
@@ -62,6 +63,17 @@ class NumberFormatter {
|
|
|
62
63
|
const formattedObject = this.format(input);
|
|
63
64
|
return formattedObject.value || '';
|
|
64
65
|
}
|
|
66
|
+
formatToParts(input) {
|
|
67
|
+
const parts = [];
|
|
68
|
+
const formatter = new NumberFormatter(Object.assign(Object.assign({}, this.options), { outputFormat: 'plain' }));
|
|
69
|
+
formatter.format(input, parts);
|
|
70
|
+
if (!parts.length) {
|
|
71
|
+
const value = formatter.toPlainString(input);
|
|
72
|
+
if (value)
|
|
73
|
+
parts.push({ type: 'literal', value });
|
|
74
|
+
}
|
|
75
|
+
return parts;
|
|
76
|
+
}
|
|
65
77
|
// Private methods...
|
|
66
78
|
isENotation(input) {
|
|
67
79
|
return /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)$/.test(input);
|
|
@@ -134,7 +146,7 @@ class NumberFormatter {
|
|
|
134
146
|
}
|
|
135
147
|
return numStr;
|
|
136
148
|
}
|
|
137
|
-
format(input) {
|
|
149
|
+
format(input, partsSink, preserveJson = false) {
|
|
138
150
|
var _a;
|
|
139
151
|
let { precision, template } = this.options;
|
|
140
152
|
const { language, outputFormat, prefixMarker, postfixMarker, prefix, postfix, thousandSeparator, decimalSeparator, } = this.options;
|
|
@@ -492,9 +504,9 @@ class NumberFormatter {
|
|
|
492
504
|
p = Math.max(p, 20);
|
|
493
505
|
r = false;
|
|
494
506
|
}
|
|
495
|
-
return this.reducePrecision(numberString, p, d, r, c, f, template, language, outputFormat, prefixMarker, postfixMarker, prefix, postfix, thousandSeparator, decimalSeparator, originalInput);
|
|
507
|
+
return this.reducePrecision(numberString, p, d, r, c, f, template, language, outputFormat, prefixMarker, postfixMarker, prefix, postfix, thousandSeparator, decimalSeparator, originalInput, partsSink, preserveJson);
|
|
496
508
|
}
|
|
497
|
-
reducePrecision(numberString, precision = 30, nonZeroDigits = 4, round = false, compress = false, fixedDecimalZeros = 0, template = 'number', language = 'en', outputFormat = 'plain', prefixMarker = 'span', postfixMarker = 'span', prefix = '', postfix = '', thousandSeparator = ',', decimalSeparator = '.', originalInput = '') {
|
|
509
|
+
reducePrecision(numberString, precision = 30, nonZeroDigits = 4, round = false, compress = false, fixedDecimalZeros = 0, template = 'number', language = 'en', outputFormat = 'plain', prefixMarker = 'span', postfixMarker = 'span', prefix = '', postfix = '', thousandSeparator = ',', decimalSeparator = '.', originalInput = '', partsSink, preserveJson = false) {
|
|
498
510
|
var _a, _b;
|
|
499
511
|
if (numberString === undefined || numberString === null || numberString.trim() === '') {
|
|
500
512
|
return {};
|
|
@@ -544,6 +556,17 @@ class NumberFormatter {
|
|
|
544
556
|
Qd: ' کادریلیون تومان',
|
|
545
557
|
Qt: ' کنتیلیون تومان',
|
|
546
558
|
};
|
|
559
|
+
// Rial has its own compact labels; Toman's "همت" is currency-specific.
|
|
560
|
+
if (template === 'irr') {
|
|
561
|
+
Object.assign(scaleUnits, {
|
|
562
|
+
K: ' هزار ر', M: ' میلیون ر', B: ' میلیارد ر',
|
|
563
|
+
T: ' هزار میلیارد ر', Qd: ' کادریلیون ر', Qt: ' کنتیلیون ر',
|
|
564
|
+
});
|
|
565
|
+
Object.assign(fullScaleUnits, {
|
|
566
|
+
K: ' هزار ریال', M: ' میلیون ریال', B: ' میلیارد ریال',
|
|
567
|
+
T: ' هزار میلیارد ریال', Qd: ' کادریلیون ریال', Qt: ' کنتیلیون ریال',
|
|
568
|
+
});
|
|
569
|
+
}
|
|
547
570
|
let parts = /^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(numberString);
|
|
548
571
|
if (!parts) {
|
|
549
572
|
return {};
|
|
@@ -667,6 +690,7 @@ class NumberFormatter {
|
|
|
667
690
|
}
|
|
668
691
|
}
|
|
669
692
|
}
|
|
693
|
+
const compactUnit = unitPostfix;
|
|
670
694
|
// Output Formating, Prefix, Postfix
|
|
671
695
|
if (template === 'usd') {
|
|
672
696
|
unitPrefix = language === 'en' ? '$' : '';
|
|
@@ -753,6 +777,48 @@ class NumberFormatter {
|
|
|
753
777
|
return String(scaleUnits[c]);
|
|
754
778
|
});
|
|
755
779
|
}
|
|
780
|
+
if (template === 'irt' && (partsSink || (outputFormat === 'html' && this.options.currencySymbol === 'svg' && !preserveJson))) {
|
|
781
|
+
const localize = (text) => language === 'fa'
|
|
782
|
+
? text.replace(/[0-9]/g, c => String.fromCharCode(c.charCodeAt(0) + 1728))
|
|
783
|
+
: text;
|
|
784
|
+
const scales = language === 'fa'
|
|
785
|
+
? { K: 'هزار', M: 'میلیون', B: 'میلیارد', T: 'هزار میلیارد', Qd: 'کادریلیون', Qt: 'کنتیلیون' }
|
|
786
|
+
: { K: 'K', M: 'M', B: 'B', T: 'T', Qd: 'Qd', Qt: 'Qt' };
|
|
787
|
+
const parts = [];
|
|
788
|
+
if (sign)
|
|
789
|
+
parts.push({ type: 'sign', value: sign });
|
|
790
|
+
if (prefix)
|
|
791
|
+
parts.push({ type: 'prefix', value: localize(prefix) });
|
|
792
|
+
parts.push({ type: 'number', value: localize(wholeNumberStr.replace(/,/g, thousandSeparator).replace(/\./g, decimalSeparator)) });
|
|
793
|
+
parts.push({ type: 'literal', value: ' ' });
|
|
794
|
+
if (compactUnit) {
|
|
795
|
+
parts.push({ type: 'compact', value: scales[compactUnit] || compactUnit });
|
|
796
|
+
parts.push({ type: 'literal', value: ' ' });
|
|
797
|
+
}
|
|
798
|
+
parts.push({ type: 'currency', value: language === 'fa' ? 'ت' : 'T' });
|
|
799
|
+
if (postfix)
|
|
800
|
+
parts.push({ type: 'postfix', value: localize(postfix) });
|
|
801
|
+
if (partsSink)
|
|
802
|
+
partsSink.push(...parts);
|
|
803
|
+
if (outputFormat === 'html' && this.options.currencySymbol === 'svg' && !preserveJson) {
|
|
804
|
+
const escapeHtml = (text) => text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
805
|
+
const wrap = (text, marker) => {
|
|
806
|
+
const tag = /^(?:i|b|em|strong|span|small|sup|sub)$/i.test(marker) ? marker : 'span';
|
|
807
|
+
return text ? `<${tag}>${text}</${tag}>` : '';
|
|
808
|
+
};
|
|
809
|
+
const numberIndex = parts.findIndex(part => part.type === 'number');
|
|
810
|
+
const suffixHtml = parts.slice(numberIndex + 1).map(part => part.type === 'currency'
|
|
811
|
+
? symbols_1.tomanSymbolSvg.replace('aria-label="تومان"', `aria-label="${language === 'fa' ? 'تومان' : 'Toman'}"`)
|
|
812
|
+
: escapeHtml(part.value)).join('');
|
|
813
|
+
formattedObject.value = escapeHtml(sign)
|
|
814
|
+
+ wrap(escapeHtml(localize(prefix)), prefixMarker)
|
|
815
|
+
+ escapeHtml(parts[numberIndex].value)
|
|
816
|
+
+ wrap(suffixHtml, postfixMarker);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
else if (partsSink && formattedObject.value) {
|
|
820
|
+
partsSink.push({ type: 'literal', value: formattedObject.value });
|
|
821
|
+
}
|
|
756
822
|
return formattedObject;
|
|
757
823
|
}
|
|
758
824
|
_sanitizeLiveInput(input) {
|
package/ts/lib/src/index.d.ts
CHANGED
package/ts/lib/src/index.js
CHANGED
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.NumberFormatter = void 0;
|
|
6
|
+
exports.tomanSymbolSvg = exports.NumberFormatter = void 0;
|
|
7
7
|
const format_1 = __importDefault(require("./format"));
|
|
8
8
|
exports.NumberFormatter = format_1.default;
|
|
9
|
+
var symbols_1 = require("./symbols");
|
|
10
|
+
Object.defineProperty(exports, "tomanSymbolSvg", { enumerable: true, get: function () { return symbols_1.tomanSymbolSvg; } });
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Inline Toman SVG; inherits the surrounding text color. */
|
|
2
|
+
export declare const tomanSymbolSvg = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" class=\"rp-currency-symbol\" role=\"img\" aria-label=\"\u062A\u0648\u0645\u0627\u0646\" focusable=\"false\" style=\"vertical-align:-0.15em\" viewBox=\"0 0 24 24\" fill=\"none\">\n<g stroke=\"currentColor\" stroke-width=\"1.65\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n<path d=\"M3 6.3v1.3c0 2.1 1.25 3.3 3.5 3.3S10 9.7 10 7.6V6.3\"/>\n<path d=\"M2.2 13.65v4.2c0 1.18.68 1.85 1.85 1.85H6.9\"/>\n<path d=\"M9.1 19.7H7.05a2.05 2.05 0 1 1 2.05-2.05v2.05Z\"/>\n<path d=\"M22 15.6v1.1c0 1.37-.82 2.2-2.25 2.2H16.5\"/>\n<path d=\"M16.5 18.7v-1.45a2.1 2.1 0 0 0-4.2 0c0 1.27.8 2 2.1 2h2.1c-.5 1.8-2.55 2.8-5.35 2.8\"/>\n</g>\n<g fill=\"currentColor\">\n<circle cx=\"6.5\" cy=\"3.55\" r=\"0.9\"/>\n<circle cx=\"18.85\" cy=\"12.75\" r=\"0.9\"/>\n<circle cx=\"21.95\" cy=\"12.75\" r=\"0.9\"/>\n</g>\n</svg>";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tomanSymbolSvg = void 0;
|
|
4
|
+
/** Inline Toman SVG; inherits the surrounding text color. */
|
|
5
|
+
exports.tomanSymbolSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" class="rp-currency-symbol" role="img" aria-label="تومان" focusable="false" style="vertical-align:-0.15em" viewBox="0 0 24 24" fill="none">\n<g stroke="currentColor" stroke-width="1.65" stroke-linecap="round" stroke-linejoin="round">\n<path d="M3 6.3v1.3c0 2.1 1.25 3.3 3.5 3.3S10 9.7 10 7.6V6.3"/>\n<path d="M2.2 13.65v4.2c0 1.18.68 1.85 1.85 1.85H6.9"/>\n<path d="M9.1 19.7H7.05a2.05 2.05 0 1 1 2.05-2.05v2.05Z"/>\n<path d="M22 15.6v1.1c0 1.37-.82 2.2-2.25 2.2H16.5"/>\n<path d="M16.5 18.7v-1.45a2.1 2.1 0 0 0-4.2 0c0 1.27.8 2 2.1 2h2.1c-.5 1.8-2.55 2.8-5.35 2.8"/>\n</g>\n<g fill="currentColor">\n<circle cx="6.5" cy="3.55" r="0.9"/>\n<circle cx="18.85" cy="12.75" r="0.9"/>\n<circle cx="21.95" cy="12.75" r="0.9"/>\n</g>\n</svg>';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const src_1 = require("../../src");
|
|
4
|
+
const scales = [
|
|
5
|
+
[1000, 'هزار'], [1e6, 'میلیون'], [1e9, 'میلیارد'],
|
|
6
|
+
[1e12, 'هزار میلیارد'], [1e15, 'کادریلیون'], [1e18, 'کنتیلیون'],
|
|
7
|
+
];
|
|
8
|
+
describe('Rial currency labels', () => {
|
|
9
|
+
for (const precision of ['high', 'medium', 'low', 'auto']) {
|
|
10
|
+
for (const [value, scale] of scales) {
|
|
11
|
+
it(`${precision}: ${value} keeps Rial across every output`, () => {
|
|
12
|
+
const formatter = new src_1.NumberFormatter({ template: 'irr', precision, currencySymbol: 'svg' }).setLanguage('fa');
|
|
13
|
+
const compact = precision === 'medium' || precision === 'low' || (precision === 'auto' && value >= 1e12);
|
|
14
|
+
const plain = formatter.toPlainString(value);
|
|
15
|
+
expect(plain).toMatch(/ ر$/);
|
|
16
|
+
expect(plain).not.toMatch(/تومان|همت| ت(?:$|\s)/);
|
|
17
|
+
if (compact)
|
|
18
|
+
expect(plain).toContain(` ${scale} ر`);
|
|
19
|
+
expect(formatter.formatToParts(value).map(p => p.value).join('')).toBe(plain);
|
|
20
|
+
expect(formatter.toHtmlString(value)).toContain('<i>' + (compact ? ` ${scale} ر` : ' ر') + '</i>');
|
|
21
|
+
expect(formatter.toMdString(value)).not.toContain(' ت');
|
|
22
|
+
const json = formatter.toJson(value);
|
|
23
|
+
expect(json.postfix).toContain(' ر');
|
|
24
|
+
if (compact)
|
|
25
|
+
expect(json.fullPostfix).toContain(` ${scale} ریال`);
|
|
26
|
+
expect(formatter.toString(value)).toBe(formatter.toMdString(value));
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
it('preserves signs, affixes, and Toman behavior', () => {
|
|
31
|
+
const f = new src_1.NumberFormatter({ template: 'irr', precision: 'medium' }).setLanguage('fa', { prefix: 'قیمت: ', postfix: ' امروز' });
|
|
32
|
+
expect(f.toPlainString(-1e6)).toBe('-قیمت: ۱٫۰۰ میلیون ر امروز');
|
|
33
|
+
expect(f.toPlainString(0)).toContain(' ر');
|
|
34
|
+
expect(f.toPlainString('')).toBe('');
|
|
35
|
+
f.setTemplate('irt', 'medium');
|
|
36
|
+
expect(f.toPlainString(1e12)).toContain(' همت');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const src_1 = require("../../src");
|
|
4
|
+
describe('Toman SVG rendering', () => {
|
|
5
|
+
const make = (svg = true) => new src_1.NumberFormatter({ template: 'irt', currencySymbol: svg ? 'svg' : 'text' }).setLanguage('fa');
|
|
6
|
+
it('keeps text, markdown, and JSON compatible even after HTML rendering', () => {
|
|
7
|
+
const icon = make();
|
|
8
|
+
const text = make(false);
|
|
9
|
+
for (const value of [0, -12500, 1e6, 1e12, '0.000001', '']) {
|
|
10
|
+
expect(icon.toPlainString(value)).toBe(text.toPlainString(value));
|
|
11
|
+
expect(icon.toMdString(value)).toBe(text.toMdString(value));
|
|
12
|
+
icon.toHtmlString(value);
|
|
13
|
+
text.toHtmlString(value);
|
|
14
|
+
expect(icon.toJson(value)).toEqual(text.toJson(value));
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
it('keeps SVG coordinates and path commands intact', () => {
|
|
18
|
+
const output = make().toHtmlString(12500);
|
|
19
|
+
expect(output).toContain(src_1.tomanSymbolSvg);
|
|
20
|
+
expect(output).toContain('۱۲');
|
|
21
|
+
expect(output).toContain('stroke-width="1.65"');
|
|
22
|
+
});
|
|
23
|
+
it('separates compact scale, currency, and affixes', () => {
|
|
24
|
+
var _a;
|
|
25
|
+
const formatter = make().setTemplate('irt', 'medium').setLanguage('fa', { prefix: 'قیمت: ', postfix: ' پایان' });
|
|
26
|
+
const parts = formatter.formatToParts(1e12);
|
|
27
|
+
expect((_a = parts.find(p => p.type === 'compact')) === null || _a === void 0 ? void 0 : _a.value).toBe('هزار میلیارد');
|
|
28
|
+
expect(parts.filter(p => p.type === 'currency')).toEqual([{ type: 'currency', value: 'ت' }]);
|
|
29
|
+
expect(parts[0]).toEqual({ type: 'prefix', value: 'قیمت: ' });
|
|
30
|
+
expect(parts[parts.length - 1]).toEqual({ type: 'postfix', value: ' پایان' });
|
|
31
|
+
});
|
|
32
|
+
it('retains supported HTML affix markers', () => {
|
|
33
|
+
const output = make().setLanguage('fa', { prefix: 'قیمت ', prefixMarker: 'strong', postfixMarker: 'span' }).toHtmlString(12);
|
|
34
|
+
expect(output).toContain('<strong>قیمت </strong>');
|
|
35
|
+
expect(output).toContain('<span> ' + src_1.tomanSymbolSvg + '</span>');
|
|
36
|
+
});
|
|
37
|
+
it('escapes user affixes in SVG HTML', () => {
|
|
38
|
+
expect(make().setLanguage('fa', { postfix: '<img src=x onerror=alert(1)>' }).toHtmlString(12)).toContain('<img');
|
|
39
|
+
});
|
|
40
|
+
it('does not affect other templates and does not mutate format during parts rendering', () => {
|
|
41
|
+
const formatter = make().setTemplate('usd', 'high');
|
|
42
|
+
expect(formatter.toHtmlString(12)).toBe(make(false).setTemplate('usd', 'high').toHtmlString(12));
|
|
43
|
+
const irt = make();
|
|
44
|
+
const before = irt.toHtmlString(12);
|
|
45
|
+
irt.formatToParts(12);
|
|
46
|
+
expect(irt.toString(12)).toBe(before);
|
|
47
|
+
expect(irt.formatToParts('')).toEqual([]);
|
|
48
|
+
expect(irt.formatToParts('bad')).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
});
|