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 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 `format` function accepts an optional `options` object with the following properties:
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.2.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
  },
@@ -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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
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) {
@@ -1,2 +1,4 @@
1
1
  import NumberFormatter from './format';
2
2
  export { NumberFormatter };
3
+ export { tomanSymbolSvg } from './symbols';
4
+ export type { FormatPart } from './format';
@@ -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('&lt;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
+ });