reduce-precision 1.0.6 → 1.1.0
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/package.json +2 -2
- package/ts/lib/bundle.min.js +1 -1
- package/ts/lib/format/index.js +73 -17
- package/ts/lib/src/format/index.d.ts +46 -0
- package/ts/lib/src/format/index.js +807 -0
- package/ts/lib/src/index.d.ts +2 -0
- package/ts/lib/src/index.js +8 -0
- package/ts/lib/test/format/index.spec.d.ts +1 -0
- package/ts/lib/test/format/index.spec.js +149 -0
package/package.json
CHANGED
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)}format(e){let{precision:t,template:r}=this.options;const{language:i,outputFormat:o,prefixMarker:n,postfixMarker:a,prefix:s,postfix:u,thousandSeparator:l,decimalSeparator:p}=this.options;if(null==e||""===e)return{};(null==r?void 0:r.match(/^(number|usd|irt|irr|percent)$/g))||(r="number"),this.isENotation(e.toString())&&(e=this.convertENotationToRegularNumber(Number(e)));let g=e.toString().replace(/[\u0660-\u0669\u06F0-\u06F9]/g,(function(e){return String(15&e.charCodeAt(0))})).replace(/[^\d.-]/g,"");g=g.replace(/^0+(?=\d)/g,"").replace(/\.\d*0+$|(\.\d)0+\b/g,"$1");const f=Math.abs(Number(g));let c,d,h,m,x=0;if("auto"===t&&(r.match(/^(usd|irt|irr|number)$/g)?t=f>=1e-4&&f<1e11?"high":"medium":"percent"===r&&(t="low")),"medium"===t)if(f>=0&&f<1e-4)c=33,d=4,h=!1,m=!0;else if(f>=1e-4&&f<.001)c=7,d=4,h=!1,m=!1;else if(f>=.001&&f<.01)c=5,d=3,h=!1,m=!1;else if(f>=.001&&f<.1)c=3,d=2,h=!1,m=!1;else if(f>=.1&&f<1)c=1,d=1,h=!1,m=!1;else if(f>=1&&f<10)c=3,d=3,h=!1,m=!1;else if(f>=10&&f<100)c=2,d=2,h=!1,m=!1;else if(f>=100&&f<1e3)c=1,d=1,h=!1,m=!1;else if(f>=1e3){const e=Math.floor(Math.log10(f))%3;c=2-e,d=2-e,h=!0,m=!0}else c=0,d=0,h=!0,m=!0;else if("low"===t)if(f>=0&&f<.01)c=2,d=0,h=!0,m=!1,x=2;else if(f>=.01&&f<.1)c=2,d=1,h=!0,m=!1;else if(f>=.1&&f<1)c=2,d=2,h=!0,m=!1;else if(f>=1&&f<10)c=2,d=2,h=!0,m=!1,x=2;else if(f>=10&&f<100)c=1,d=1,h=!0,m=!1,x=1;else if(f>=100&&f<1e3)c=0,d=0,h=!0,m=!1;else if(f>=1e3){const e=Math.floor(Math.log10(f))%3;c=1-e,d=1-e,h=!0,m=!0}else c=0,d=0,h=!0,m=!0,x=2;else f>=0&&f<1?(c=33,d=4,h=!1,m=!1):f>=1&&f<10?(c=3,d=3,h=!0,m=!1):f>=10&&f<100||f>=100&&f<1e3?(c=2,d=2,h=!0,m=!1):f>=1e3&&f<1e4?(c=1,d=1,h=!0,m=!1):(c=0,d=0,h=!0,m=!1);return this.reducePrecision(g,c,d,h,m,x,r,i,o,n,a,s,u,l,p)}convertENotationToRegularNumber(e){const[t,r]=e.toString().split("e"),i=t.replace(".","").replace("-","").length,o=parseFloat(r),n=Math.max(i-o,1);return e.toFixed(n)}reducePrecision(e,t=30,r=4,i=!1,o=!1,n=0,a="number",s="en",u="plain",l="span",p="span",g="",f="",c=",",d="."){var h,m;if(null==e||""===e.trim())return{};e=e.toString();const x=a.match(/^(number|percent)$/g)?{"":"",K:" هزار",M:" میلیون",B:" میلیارد",T:" تریلیون",Qd:" کادریلیون",Qt:" کنتیلیون"}:{"":"",K:" هزار ت",M:" میلیون ت",B:" میلیارد ت",T:" همت",Qd:" هزار همت",Qt:" میلیون همت"},b=a.match(/^(number|percent)$/g)?{"":"",K:" هزار",M:" میلیون",B:" میلیارد",T:" تریلیون",Qd:" کادریلیون",Qt:" کنتیلیون"}:{"":"",K:" هزار تومان",M:" میلیون تومان",B:" میلیارد تومان",T:" هزار میلیارد تومان",Qd:" کادریلیون تومان",Qt:" کنتیلیون تومان"};let $=/^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(e);if(!$)return{};const S=$[1]||"";let M=$[2],v=$[3],C=$[4],Q="",N="";if(v.length>=30?(v="0".padEnd(29,"0"),C="1"):v.length+r>t?(r=t-v.length)<1&&(r=1):M.length>21&&(M="0",v="",C=""),o&&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(N=e[r],$=/^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(t.toString()),!$)return{};M=$[2],v=$[3],C=$[4]}C.length>r&&(i?parseInt(C[r])<5?C=C.substring(0,r):(C=(parseInt(C.substring(0,r))+1).toString(),C.length>r&&(v.length>0?v=v.substring(0,v.length-1):(M=(Number(M)+1).toString(),C=C.substring(1)))):C=C.substring(0,r)),o&&""!==v&&""===N&&(v="0"+v.length.toString().replace(/\d/g,(function(e){return["₀","₁","₂","₃","₄","₅","₆","₇","₈","₉"][parseInt(e,10)]})));let j=`${v}${C}`;j=j.substring(0,t),j=j.replace(/^(\d*[1-9])0+$/g,"$1"),"usd"===a?(Q="en"===s?"$":"",N||(N="fa"===s?" دلار":"")):"irr"===a?N||(N="fa"===s?" ر":" R"):"irt"===a?N||(N="fa"===s?" ت":" T"):"percent"===a&&(N+="en"===s?"%":N?" درصد":"٪"),Q=g+Q,N+=f,"html"===u?(Q&&(Q=`<${l}>${Q}</${l}>`),N&&(N=`<${p}>${N}</${p}>`)):"markdown"===u&&(Q&&(Q=`${l}${Q}${l}`),N&&(N=`${p}${N}${p}`));const k=/\B(?=(\d{3})+(?!\d))/g,B=n?".".padEnd(n+1,"0"):"";let F,T="";F=t<=0||r<=0||!C?`${M.replace(k,",")}${B}`:`${M.replace(k,",")}.${j}`,T=`${S}${Q}${F}${N}`;const O={value:T,prefix:Q,postfix:N,sign:S,wholeNumber:F};return O.value=(null!==(h=null==O?void 0:O.value)&&void 0!==h?h:"").replace(/,/g,c).replace(/\./g,d),"fa"===s&&(O.value=(null!==(m=null==O?void 0:O.value)&&void 0!==m?m:"").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])})),O.fullPostfix=N.replace(/[0-9]/g,(e=>String.fromCharCode(e.charCodeAt(0)+1728))).replace(/(K|M|B|T|Qt|Qd)/g,(function(e){return String(b[e])})),O.postfix=O.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])})),O.wholeNumber=O.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])}))),O}}},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 o=i(r(900));t.NumberFormatter=o.default}},t={};return function r(i){var o=t[i];if(void 0!==o)return o.exports;var n=t[i]={exports:{}};return e[i].call(n.exports,n,n.exports,r),n.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={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)})()));
|
package/ts/lib/format/index.js
CHANGED
|
@@ -64,7 +64,7 @@ class NumberFormatter {
|
|
|
64
64
|
}
|
|
65
65
|
// Private methods...
|
|
66
66
|
isENotation(input) {
|
|
67
|
-
return /^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)$/.test(input);
|
|
67
|
+
return /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)$/.test(input);
|
|
68
68
|
}
|
|
69
69
|
format(input) {
|
|
70
70
|
let { precision, template } = this.options;
|
|
@@ -74,7 +74,9 @@ class NumberFormatter {
|
|
|
74
74
|
}
|
|
75
75
|
if (!(template === null || template === void 0 ? void 0 : template.match(/^(number|usd|irt|irr|percent)$/g)))
|
|
76
76
|
template = 'number';
|
|
77
|
-
|
|
77
|
+
// Store original input string to preserve format for trailing zeros
|
|
78
|
+
const originalInput = input.toString();
|
|
79
|
+
if (this.isENotation(originalInput)) {
|
|
78
80
|
input = this.convertENotationToRegularNumber(Number(input));
|
|
79
81
|
}
|
|
80
82
|
// Replace each Persian/Arabic numeral in the string with its English counterpart and strip all non-numeric chars
|
|
@@ -84,10 +86,9 @@ class NumberFormatter {
|
|
|
84
86
|
return String(match.charCodeAt(0) & 0xf);
|
|
85
87
|
})
|
|
86
88
|
.replace(/[^\d.-]/g, '');
|
|
87
|
-
// Stripping leading zeros
|
|
89
|
+
// Stripping leading zeros only, preserve trailing zeros
|
|
88
90
|
numberString = numberString
|
|
89
|
-
.replace(/^0+(?=\d)/g, '')
|
|
90
|
-
.replace(/\.\d*0+$|(\.\d)0+\b/g, '$1');
|
|
91
|
+
.replace(/^0+(?=\d)/g, '');
|
|
91
92
|
const number = Math.abs(Number(numberString));
|
|
92
93
|
let p, d, r, c;
|
|
93
94
|
let f = 0;
|
|
@@ -262,22 +263,43 @@ class NumberFormatter {
|
|
|
262
263
|
c = false;
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
|
-
|
|
266
|
+
// For scientific notation, increase precision to ensure correct representation
|
|
267
|
+
if (this.isENotation(originalInput)) {
|
|
268
|
+
p = Math.max(p, 20);
|
|
269
|
+
r = false;
|
|
270
|
+
}
|
|
271
|
+
return this.reducePrecision(numberString, p, d, r, c, f, template, language, outputFormat, prefixMarker, postfixMarker, prefix, postfix, thousandSeparator, decimalSeparator, originalInput);
|
|
266
272
|
}
|
|
267
273
|
convertENotationToRegularNumber(eNotation) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
.
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
274
|
+
// For simple cases like 1e3, directly use Number constructor
|
|
275
|
+
if (Number.isInteger(eNotation) && eNotation >= 1000) {
|
|
276
|
+
return eNotation.toString();
|
|
277
|
+
}
|
|
278
|
+
const parts = eNotation.toString().toLowerCase().split('e');
|
|
279
|
+
if (parts.length !== 2)
|
|
280
|
+
return eNotation.toString();
|
|
281
|
+
const coefficient = parseFloat(parts[0]);
|
|
282
|
+
const exponent = parseInt(parts[1], 10);
|
|
283
|
+
// Handle negative exponents (very small numbers)
|
|
284
|
+
if (exponent < 0) {
|
|
285
|
+
const absExponent = Math.abs(exponent);
|
|
286
|
+
// Determine precision needed to show all digits
|
|
287
|
+
const precision = absExponent +
|
|
288
|
+
(parts[0].includes('.') ? parts[0].split('.')[1].length : 0);
|
|
289
|
+
return eNotation.toFixed(precision);
|
|
290
|
+
}
|
|
291
|
+
// For positive exponents, let JavaScript do the conversion
|
|
292
|
+
return eNotation.toString();
|
|
275
293
|
}
|
|
276
|
-
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 = '.') {
|
|
294
|
+
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 = '') {
|
|
277
295
|
var _a, _b;
|
|
278
296
|
if (numberString === undefined || numberString === null || numberString.trim() === '') {
|
|
279
297
|
return {};
|
|
280
298
|
}
|
|
299
|
+
// Handle negative zero
|
|
300
|
+
if (numberString === '-0' || numberString === '-0.0') {
|
|
301
|
+
numberString = numberString.substring(1); // Remove negative sign for zero
|
|
302
|
+
}
|
|
281
303
|
numberString = numberString.toString();
|
|
282
304
|
const maxPrecision = 30;
|
|
283
305
|
const maxIntegerDigits = 21;
|
|
@@ -408,9 +430,40 @@ class NumberFormatter {
|
|
|
408
430
|
][parseInt(match, 10)];
|
|
409
431
|
});
|
|
410
432
|
}
|
|
433
|
+
// Check if the original input had trailing zeros
|
|
411
434
|
let fractionalPartStr = `${fractionalZeroStr}${fractionalNonZeroStr}`;
|
|
412
|
-
|
|
413
|
-
fractionalPartStr
|
|
435
|
+
// Don't truncate trailing zeros when they're in the original string
|
|
436
|
+
if (fractionalPartStr.length > precision && !originalInput.includes('e')) {
|
|
437
|
+
fractionalPartStr = fractionalPartStr.substring(0, precision);
|
|
438
|
+
}
|
|
439
|
+
// For scientific notation and numbers with trailing zeros, preserve the format
|
|
440
|
+
if (originalInput.includes('e') || originalInput.includes('E')) {
|
|
441
|
+
// For scientific notation, use the converted string
|
|
442
|
+
}
|
|
443
|
+
else if (originalInput.includes('.')) {
|
|
444
|
+
// For regular numbers with decimal point, check for trailing zeros
|
|
445
|
+
const originalParts = originalInput.split('.');
|
|
446
|
+
if (originalParts.length === 2) {
|
|
447
|
+
const originalDecimal = originalParts[1];
|
|
448
|
+
// If original has more digits than what we have now, preserve those trailing zeros
|
|
449
|
+
if (originalDecimal.length > fractionalPartStr.length && originalDecimal.endsWith('0')) {
|
|
450
|
+
// Count trailing zeros in original
|
|
451
|
+
let trailingZeros = 0;
|
|
452
|
+
for (let i = originalDecimal.length - 1; i >= 0; i--) {
|
|
453
|
+
if (originalDecimal[i] === '0') {
|
|
454
|
+
trailingZeros++;
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Add back trailing zeros if they were in the original
|
|
461
|
+
if (trailingZeros > 0) {
|
|
462
|
+
fractionalPartStr = fractionalPartStr.padEnd(fractionalPartStr.length + trailingZeros, '0');
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
414
467
|
// Output Formating, Prefix, Postfix
|
|
415
468
|
if (template === 'usd') {
|
|
416
469
|
unitPrefix = language === 'en' ? '$' : '';
|
|
@@ -453,7 +506,10 @@ class NumberFormatter {
|
|
|
453
506
|
: '';
|
|
454
507
|
let out = '';
|
|
455
508
|
let wholeNumberStr;
|
|
456
|
-
|
|
509
|
+
// FIXED: Changed condition to correctly handle numbers with trailing zeros
|
|
510
|
+
// Old condition: if (precision <= 0 || nonZeroDigits <= 0 || !fractionalNonZeroStr) {
|
|
511
|
+
// New condition checks if both fractional parts are empty
|
|
512
|
+
if (precision <= 0 || nonZeroDigits <= 0 || (fractionalNonZeroStr === '' && fractionalZeroStr === '')) {
|
|
457
513
|
wholeNumberStr = `${nonFractionalStr.replace(thousandSeparatorRegex, ',')}${fixedDecimalZeroStr}`;
|
|
458
514
|
}
|
|
459
515
|
else {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
type Template = 'number' | 'usd' | 'irt' | 'irr' | 'percent' | 'liveformat';
|
|
2
|
+
type Precision = 'auto' | 'high' | 'medium' | 'low';
|
|
3
|
+
type Language = 'en' | 'fa';
|
|
4
|
+
type OutputFormat = 'plain' | 'html' | 'markdown';
|
|
5
|
+
interface FormattedObject {
|
|
6
|
+
value?: string;
|
|
7
|
+
prefix: string;
|
|
8
|
+
postfix: string;
|
|
9
|
+
fullPostfix?: string;
|
|
10
|
+
sign: string;
|
|
11
|
+
wholeNumber: string;
|
|
12
|
+
}
|
|
13
|
+
interface LanguageConfig {
|
|
14
|
+
prefixMarker?: string;
|
|
15
|
+
postfixMarker?: string;
|
|
16
|
+
prefix?: string;
|
|
17
|
+
postfix?: string;
|
|
18
|
+
thousandSeparator?: string;
|
|
19
|
+
decimalSeparator?: string;
|
|
20
|
+
}
|
|
21
|
+
interface Options extends LanguageConfig {
|
|
22
|
+
precision?: Precision;
|
|
23
|
+
template?: Template;
|
|
24
|
+
language?: Language;
|
|
25
|
+
outputFormat?: OutputFormat;
|
|
26
|
+
}
|
|
27
|
+
declare class NumberFormatter {
|
|
28
|
+
private readonly languageBaseConfig;
|
|
29
|
+
private defaultLanguageConfig;
|
|
30
|
+
private options;
|
|
31
|
+
constructor(options?: Options);
|
|
32
|
+
setLanguage(lang: Language, config?: LanguageConfig): NumberFormatter;
|
|
33
|
+
setTemplate(template: Template, precision: Precision): NumberFormatter;
|
|
34
|
+
toJson(input: string | number): FormattedObject;
|
|
35
|
+
toString(input: string | number): string;
|
|
36
|
+
toPlainString(input: string | number): string;
|
|
37
|
+
toHtmlString(input: string | number): string;
|
|
38
|
+
toMdString(input: string | number): string;
|
|
39
|
+
private isENotation;
|
|
40
|
+
private convertENotationToRegularNumber;
|
|
41
|
+
private format;
|
|
42
|
+
private reducePrecision;
|
|
43
|
+
private _sanitizeLiveInput;
|
|
44
|
+
private _convertToFarsiDigits;
|
|
45
|
+
}
|
|
46
|
+
export default NumberFormatter;
|
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class NumberFormatter {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.languageBaseConfig = {
|
|
6
|
+
prefixMarker: 'i',
|
|
7
|
+
postfixMarker: 'i',
|
|
8
|
+
prefix: '',
|
|
9
|
+
postfix: '',
|
|
10
|
+
};
|
|
11
|
+
this.defaultLanguageConfig = {
|
|
12
|
+
en: Object.assign(Object.assign({}, this.languageBaseConfig), { thousandSeparator: ',', decimalSeparator: '.' }),
|
|
13
|
+
fa: Object.assign(Object.assign({}, this.languageBaseConfig), { thousandSeparator: '٬', decimalSeparator: '٫' }),
|
|
14
|
+
};
|
|
15
|
+
this.options = Object.assign({ language: 'en', template: 'number', precision: 'high', outputFormat: 'plain' }, this.defaultLanguageConfig['en']);
|
|
16
|
+
this.options = Object.assign(Object.assign({}, this.options), options);
|
|
17
|
+
}
|
|
18
|
+
setLanguage(lang, config = {}) {
|
|
19
|
+
this.options.language = lang;
|
|
20
|
+
this.options.prefixMarker =
|
|
21
|
+
config.prefixMarker || this.defaultLanguageConfig[lang].prefixMarker;
|
|
22
|
+
this.options.postfixMarker =
|
|
23
|
+
config.postfixMarker || this.defaultLanguageConfig[lang].postfixMarker;
|
|
24
|
+
this.options.prefix =
|
|
25
|
+
config.prefix || this.defaultLanguageConfig[lang].prefix;
|
|
26
|
+
this.options.postfix =
|
|
27
|
+
config.postfix || this.defaultLanguageConfig[lang].postfix;
|
|
28
|
+
this.options.thousandSeparator =
|
|
29
|
+
config.thousandSeparator ||
|
|
30
|
+
this.defaultLanguageConfig[lang].thousandSeparator;
|
|
31
|
+
this.options.decimalSeparator =
|
|
32
|
+
config.decimalSeparator ||
|
|
33
|
+
this.defaultLanguageConfig[lang].decimalSeparator;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
setTemplate(template, precision) {
|
|
37
|
+
this.options.template = template;
|
|
38
|
+
this.options.precision = precision;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
toJson(input) {
|
|
42
|
+
const formattedObject = this.format(input);
|
|
43
|
+
delete formattedObject.value;
|
|
44
|
+
return formattedObject;
|
|
45
|
+
}
|
|
46
|
+
toString(input) {
|
|
47
|
+
const formattedObject = this.format(input);
|
|
48
|
+
return formattedObject.value || '';
|
|
49
|
+
}
|
|
50
|
+
toPlainString(input) {
|
|
51
|
+
this.options.outputFormat = 'plain';
|
|
52
|
+
const formattedObject = this.format(input);
|
|
53
|
+
return formattedObject.value || '';
|
|
54
|
+
}
|
|
55
|
+
toHtmlString(input) {
|
|
56
|
+
this.options.outputFormat = 'html';
|
|
57
|
+
const formattedObject = this.format(input);
|
|
58
|
+
return formattedObject.value || '';
|
|
59
|
+
}
|
|
60
|
+
toMdString(input) {
|
|
61
|
+
this.options.outputFormat = 'markdown';
|
|
62
|
+
const formattedObject = this.format(input);
|
|
63
|
+
return formattedObject.value || '';
|
|
64
|
+
}
|
|
65
|
+
// Private methods...
|
|
66
|
+
isENotation(input) {
|
|
67
|
+
return /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)$/.test(input);
|
|
68
|
+
}
|
|
69
|
+
// Modify to accept originalEString to handle cases like "0.0e0" -> "0.0"
|
|
70
|
+
convertENotationToRegularNumber(num, originalEString) {
|
|
71
|
+
let numStr = String(num);
|
|
72
|
+
// If an originalEString is provided and represents 0 but has a specific format like "0.0", prefer that.
|
|
73
|
+
if (num === 0 && originalEString) {
|
|
74
|
+
const ePartsOriginal = originalEString.toLowerCase().split('e');
|
|
75
|
+
if (ePartsOriginal.length === 2) {
|
|
76
|
+
// Check if coefficient was like "0.0", "0.00" etc.
|
|
77
|
+
if (ePartsOriginal[0].match(/^0\.0*$/)) {
|
|
78
|
+
return ePartsOriginal[0]; // Return "0.0" or "0.00" etc.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// If not E-notation according to JS default toString for the number, or if it's simple "0", return it.
|
|
83
|
+
if (numStr.toLowerCase().indexOf('e') === -1) {
|
|
84
|
+
return numStr;
|
|
85
|
+
}
|
|
86
|
+
// If it IS E-notation, attempt to convert to plain decimal string.
|
|
87
|
+
// Handle very small numbers (negative exponent) using toFixed.
|
|
88
|
+
// Check num !== 0 because String(0) is "0", which doesn't contain 'e'.
|
|
89
|
+
if (Math.abs(num) < 1.0 && num !== 0) {
|
|
90
|
+
const eParts = numStr.toLowerCase().split('e'); // Use numStr here, not originalEString unless passed carefully
|
|
91
|
+
// Ensure it's a valid E-notation string with a negative exponent
|
|
92
|
+
if (eParts.length === 2) {
|
|
93
|
+
const exponent = parseInt(eParts[1], 10);
|
|
94
|
+
if (exponent < 0) {
|
|
95
|
+
let precision = Math.abs(exponent);
|
|
96
|
+
// Use coefficient from numStr for precision calculation
|
|
97
|
+
if (eParts[0].includes('.')) {
|
|
98
|
+
precision += eParts[0].split('.')[1].length;
|
|
99
|
+
}
|
|
100
|
+
// Cap precision to prevent overly long strings or errors with toFixed.
|
|
101
|
+
return num.toFixed(Math.min(precision, 100));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Handle large numbers or numbers with positive E-notation if not caught above.
|
|
106
|
+
// This part manually reconstructs the string from numStr.
|
|
107
|
+
const parts = numStr.toLowerCase().split('e');
|
|
108
|
+
if (parts.length === 2) {
|
|
109
|
+
const coefficientStr = parts[0];
|
|
110
|
+
const exponent = parseInt(parts[1], 10);
|
|
111
|
+
let [integer, fraction] = coefficientStr.split('.');
|
|
112
|
+
fraction = fraction || '';
|
|
113
|
+
if (exponent > 0) { // Positive exponent
|
|
114
|
+
const sign = integer.startsWith('-') ? "-" : "";
|
|
115
|
+
if (sign)
|
|
116
|
+
integer = integer.substring(1);
|
|
117
|
+
if (fraction.length <= exponent) {
|
|
118
|
+
return sign + integer + fraction.padEnd(exponent, '0');
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
return sign + integer + fraction.substring(0, exponent) + '.' + fraction.substring(exponent);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else if (exponent < 0) { // Should have been caught by toFixed if num was < 1.0
|
|
125
|
+
let precision = Math.abs(exponent);
|
|
126
|
+
if (coefficientStr.includes('.')) {
|
|
127
|
+
precision += coefficientStr.split('.')[1].length;
|
|
128
|
+
}
|
|
129
|
+
return num.toFixed(Math.min(precision, 100));
|
|
130
|
+
}
|
|
131
|
+
else { // exponent === 0
|
|
132
|
+
return coefficientStr;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return numStr;
|
|
136
|
+
}
|
|
137
|
+
format(input) {
|
|
138
|
+
var _a;
|
|
139
|
+
let { precision, template } = this.options;
|
|
140
|
+
const { language, outputFormat, prefixMarker, postfixMarker, prefix, postfix, thousandSeparator, decimalSeparator, } = this.options;
|
|
141
|
+
if (input === undefined || input === null || input === '') {
|
|
142
|
+
return {};
|
|
143
|
+
}
|
|
144
|
+
// Store original input string to preserve format for trailing zeros
|
|
145
|
+
const originalInput = input.toString();
|
|
146
|
+
if (template === 'liveformat') {
|
|
147
|
+
let currentInput = this._sanitizeLiveInput(originalInput);
|
|
148
|
+
// Define locale-specific separators early
|
|
149
|
+
const currentThousandSeparator = thousandSeparator || ',';
|
|
150
|
+
const currentDecimalSeparator = decimalSeparator || '.';
|
|
151
|
+
// Special override for inputs like "0.0e0" or "۰٫۰e0" which _sanitizeLiveInput might turn into "0".
|
|
152
|
+
// This override aims to restore the "0.0" or "0٫0" representation if originalInput implies it.
|
|
153
|
+
// First, create a version of originalInput with Western digits and '.' as decimal for reliable parsing.
|
|
154
|
+
let westernizedOriginalInput = originalInput.replace(/[٠-٩۰-۹]/g, function (match) {
|
|
155
|
+
return String(match.charCodeAt(0) & 0xf);
|
|
156
|
+
});
|
|
157
|
+
if (((_a = this.options.language) !== null && _a !== void 0 ? _a : 'en') === 'fa') {
|
|
158
|
+
const farsiDecimal = this.defaultLanguageConfig.fa.decimalSeparator || '٫';
|
|
159
|
+
westernizedOriginalInput = westernizedOriginalInput.replace(new RegExp(farsiDecimal, 'g'), '.');
|
|
160
|
+
}
|
|
161
|
+
if (this.isENotation(westernizedOriginalInput)) {
|
|
162
|
+
const numValOriginal = Number(westernizedOriginalInput);
|
|
163
|
+
if (numValOriginal === 0) {
|
|
164
|
+
const eIndex = originalInput.toLowerCase().indexOf('e');
|
|
165
|
+
if (eIndex > -1) {
|
|
166
|
+
const partBeforeEOriginal = originalInput.substring(0, eIndex);
|
|
167
|
+
const partBeforeEWesternDigits = partBeforeEOriginal.replace(/[٠-٩۰-۹]/g, function (match) {
|
|
168
|
+
return String(match.charCodeAt(0) & 0xf);
|
|
169
|
+
});
|
|
170
|
+
// Check if this part (e.g., "0.0" or "0٫0") matches the "zero with explicit decimals" pattern
|
|
171
|
+
// Normalize its decimal separator to currentDecimalSeparator for consistency before assigning to currentInput
|
|
172
|
+
let partToTest = partBeforeEWesternDigits;
|
|
173
|
+
if (partBeforeEWesternDigits.includes('.') && currentDecimalSeparator !== '.') {
|
|
174
|
+
partToTest = partBeforeEWesternDigits.replace('.', currentDecimalSeparator);
|
|
175
|
+
}
|
|
176
|
+
else if (partBeforeEWesternDigits.includes('٫') && currentDecimalSeparator !== '٫') {
|
|
177
|
+
// This case should ideally not happen if Farsi decimal is '٫' and currentDecimalSeparator is also '٫'
|
|
178
|
+
partToTest = partBeforeEWesternDigits.replace('٫', currentDecimalSeparator);
|
|
179
|
+
}
|
|
180
|
+
const zeroWithDecimalsPattern = new RegExp(`^0\\${currentDecimalSeparator}0*$`);
|
|
181
|
+
if (partToTest.match(zeroWithDecimalsPattern)) {
|
|
182
|
+
currentInput = partToTest; // currentInput is now "0[currentSeparator]0..." e.g. "0.0" or "0٫0"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
let sign = '';
|
|
188
|
+
if (currentInput.startsWith('-')) {
|
|
189
|
+
sign = '-';
|
|
190
|
+
currentInput = currentInput.substring(1); // currentInput is now the absolute numeric string
|
|
191
|
+
}
|
|
192
|
+
// const currentThousandSeparator = thousandSeparator || ','; // Moved up
|
|
193
|
+
// const currentDecimalSeparator = decimalSeparator || '.'; // Moved up
|
|
194
|
+
if (currentInput === '') {
|
|
195
|
+
return {
|
|
196
|
+
value: '',
|
|
197
|
+
prefix: '',
|
|
198
|
+
postfix: '',
|
|
199
|
+
sign: '',
|
|
200
|
+
wholeNumber: ''
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Handles "0" (from input "0" or "-0")
|
|
204
|
+
if (currentInput === '0') {
|
|
205
|
+
let outputZero = '0';
|
|
206
|
+
if (this.options.language === 'fa') {
|
|
207
|
+
outputZero = String.fromCharCode('0'.charCodeAt(0) + 1728); // Converts '0' to '۰'
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
value: outputZero,
|
|
211
|
+
prefix: '',
|
|
212
|
+
postfix: '',
|
|
213
|
+
sign: '',
|
|
214
|
+
wholeNumber: outputZero,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
// Handle cases like "." or "0." or ".0"
|
|
218
|
+
// If input is just the decimal separator, treat as "0" + separator
|
|
219
|
+
if (currentInput === currentDecimalSeparator) {
|
|
220
|
+
currentInput = '0' + currentDecimalSeparator;
|
|
221
|
+
}
|
|
222
|
+
let integerPart = '';
|
|
223
|
+
let decimalPart = '';
|
|
224
|
+
let hasDecimalPoint = false;
|
|
225
|
+
// Try splitting with current locale's decimal separator first
|
|
226
|
+
if (currentInput.includes(currentDecimalSeparator)) {
|
|
227
|
+
hasDecimalPoint = true;
|
|
228
|
+
const parts = currentInput.split(currentDecimalSeparator);
|
|
229
|
+
integerPart = parts[0];
|
|
230
|
+
decimalPart = parts.length > 1 ? parts[1] : '';
|
|
231
|
+
}
|
|
232
|
+
// If not found, and current locale is not using '.', check for '.' as a fallback
|
|
233
|
+
else if (currentDecimalSeparator !== '.' && currentInput.includes('.')) {
|
|
234
|
+
hasDecimalPoint = true;
|
|
235
|
+
const parts = currentInput.split('.');
|
|
236
|
+
integerPart = parts[0];
|
|
237
|
+
decimalPart = parts.length > 1 ? parts[1] : '';
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// No decimal point found (neither locale specific nor '.')
|
|
241
|
+
integerPart = currentInput;
|
|
242
|
+
}
|
|
243
|
+
// Now integerPart is the true integer part (still needs leading zero trim).
|
|
244
|
+
if (integerPart === '') {
|
|
245
|
+
integerPart = '0';
|
|
246
|
+
}
|
|
247
|
+
else if (integerPart !== '0') {
|
|
248
|
+
const tempInt = integerPart.replace(/^0+/, '');
|
|
249
|
+
integerPart = (tempInt === '') ? '0' : tempInt;
|
|
250
|
+
}
|
|
251
|
+
// No change if integerPart is already "0"
|
|
252
|
+
const formattedIntegerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, currentThousandSeparator);
|
|
253
|
+
let finalValue = formattedIntegerPart;
|
|
254
|
+
if (hasDecimalPoint) {
|
|
255
|
+
finalValue += currentDecimalSeparator + decimalPart;
|
|
256
|
+
}
|
|
257
|
+
const finalNumericValue = finalValue; // finalValue at this point is the formatted absolute number
|
|
258
|
+
let resultSign = '';
|
|
259
|
+
let resultValue = finalNumericValue;
|
|
260
|
+
if (sign === '-') {
|
|
261
|
+
// Only add sign if the number isn't representing plain zero.
|
|
262
|
+
// parseFloat "0.00" is 0. "0" is 0.
|
|
263
|
+
// We want "-0.123", but "0" for "-0".
|
|
264
|
+
// We want "-0.00" for input "-0.00" (if that's a required display for zero value with decimals)
|
|
265
|
+
// The tests for "-0.000000123" expect "-0.000000123".
|
|
266
|
+
// The tests for 0.000000123 expect "0.000000123".
|
|
267
|
+
// Test for -0 will clarify if it should be "0" or "-0". Assume "0".
|
|
268
|
+
if (parseFloat(finalNumericValue) === 0 && !finalNumericValue.includes('.')) { // handles "0"
|
|
269
|
+
resultSign = ''; // Make "-0" become "0"
|
|
270
|
+
// resultValue is already "0"
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
resultSign = '-';
|
|
274
|
+
resultValue = sign + finalNumericValue;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// At this point, finalValue is the formatted absolute number string (Western digits, Farsi/default separators).
|
|
278
|
+
// sign is the extracted input sign ('-' or '').
|
|
279
|
+
// Convert finalValue to Farsi digits if necessary for the absolute part.
|
|
280
|
+
const outputFormattedAbsoluteValue = this._convertToFarsiDigits(finalValue);
|
|
281
|
+
let outputSign = '';
|
|
282
|
+
// Determine the final sign string.
|
|
283
|
+
// `finalValue` is absolute, western digits, locale separator e.g. "0", "0٫0", "0٫12", "123٫45"
|
|
284
|
+
// `sign` is the original sign from input, like "-"
|
|
285
|
+
if (sign === '-') {
|
|
286
|
+
if (finalValue === '0') { // Check if the absolute formatted value is literally "0"
|
|
287
|
+
outputSign = ''; // Only for "-0" input that results in "0" absolute
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
outputSign = '-'; // For all other cases like "-0.0", "-0.12", "-123"
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
value: outputSign + outputFormattedAbsoluteValue,
|
|
295
|
+
prefix: '',
|
|
296
|
+
postfix: '',
|
|
297
|
+
sign: outputSign,
|
|
298
|
+
wholeNumber: outputFormattedAbsoluteValue, // Store Farsi/Western formatted absolute value (unsigned)
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (!(template === null || template === void 0 ? void 0 : template.match(/^(number|usd|irt|irr|percent|liveformat)$/g)))
|
|
302
|
+
template = 'number';
|
|
303
|
+
if (this.isENotation(originalInput)) {
|
|
304
|
+
input = this.convertENotationToRegularNumber(Number(input));
|
|
305
|
+
}
|
|
306
|
+
// Replace each Persian/Arabic numeral in the string with its English counterpart and strip all non-numeric chars
|
|
307
|
+
let numberString = input
|
|
308
|
+
.toString()
|
|
309
|
+
.replace(/[\u0660-\u0669\u06F0-\u06F9]/g, function (match) {
|
|
310
|
+
return String(match.charCodeAt(0) & 0xf);
|
|
311
|
+
})
|
|
312
|
+
.replace(/[^\d.-]/g, '');
|
|
313
|
+
// Stripping leading zeros only, preserve trailing zeros
|
|
314
|
+
numberString = numberString
|
|
315
|
+
.replace(/^0+(?=\d)/g, '');
|
|
316
|
+
const number = Math.abs(Number(numberString));
|
|
317
|
+
let p, d, r, c;
|
|
318
|
+
let f = 0;
|
|
319
|
+
// Auto precision selection
|
|
320
|
+
if (precision === 'auto') {
|
|
321
|
+
if (template.match(/^(usd|irt|irr|number)$/g)) {
|
|
322
|
+
if (number >= 0.0001 && number < 100000000000) {
|
|
323
|
+
precision = 'high';
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
precision = 'medium';
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else if (template === 'percent') {
|
|
330
|
+
precision = 'low';
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (precision === 'medium') {
|
|
334
|
+
if (number >= 0 && number < 0.0001) {
|
|
335
|
+
p = 33;
|
|
336
|
+
d = 4;
|
|
337
|
+
r = false;
|
|
338
|
+
c = true;
|
|
339
|
+
}
|
|
340
|
+
else if (number >= 0.0001 && number < 0.001) {
|
|
341
|
+
p = 7;
|
|
342
|
+
d = 4;
|
|
343
|
+
r = false;
|
|
344
|
+
c = false;
|
|
345
|
+
}
|
|
346
|
+
else if (number >= 0.001 && number < 0.01) {
|
|
347
|
+
p = 5;
|
|
348
|
+
d = 3;
|
|
349
|
+
r = false;
|
|
350
|
+
c = false;
|
|
351
|
+
}
|
|
352
|
+
else if (number >= 0.001 && number < 0.1) {
|
|
353
|
+
p = 3;
|
|
354
|
+
d = 2;
|
|
355
|
+
r = false;
|
|
356
|
+
c = false;
|
|
357
|
+
}
|
|
358
|
+
else if (number >= 0.1 && number < 1) {
|
|
359
|
+
p = 1;
|
|
360
|
+
d = 1;
|
|
361
|
+
r = false;
|
|
362
|
+
c = false;
|
|
363
|
+
}
|
|
364
|
+
else if (number >= 1 && number < 10) {
|
|
365
|
+
p = 3;
|
|
366
|
+
d = 3;
|
|
367
|
+
r = false;
|
|
368
|
+
c = false;
|
|
369
|
+
}
|
|
370
|
+
else if (number >= 10 && number < 100) {
|
|
371
|
+
p = 2;
|
|
372
|
+
d = 2;
|
|
373
|
+
r = false;
|
|
374
|
+
c = false;
|
|
375
|
+
}
|
|
376
|
+
else if (number >= 100 && number < 1000) {
|
|
377
|
+
p = 1;
|
|
378
|
+
d = 1;
|
|
379
|
+
r = false;
|
|
380
|
+
c = false;
|
|
381
|
+
}
|
|
382
|
+
else if (number >= 1000) {
|
|
383
|
+
const x = Math.floor(Math.log10(number)) % 3;
|
|
384
|
+
p = 2 - x;
|
|
385
|
+
d = 2 - x;
|
|
386
|
+
r = true;
|
|
387
|
+
c = true;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
p = 0;
|
|
391
|
+
d = 0;
|
|
392
|
+
r = true;
|
|
393
|
+
c = true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
else if (precision === 'low') {
|
|
397
|
+
if (number >= 0 && number < 0.01) {
|
|
398
|
+
p = 2;
|
|
399
|
+
d = 0;
|
|
400
|
+
r = true;
|
|
401
|
+
c = false;
|
|
402
|
+
f = 2;
|
|
403
|
+
}
|
|
404
|
+
else if (number >= 0.01 && number < 0.1) {
|
|
405
|
+
p = 2;
|
|
406
|
+
d = 1;
|
|
407
|
+
r = true;
|
|
408
|
+
c = false;
|
|
409
|
+
}
|
|
410
|
+
else if (number >= 0.1 && number < 1) {
|
|
411
|
+
p = 2;
|
|
412
|
+
d = 2;
|
|
413
|
+
r = true;
|
|
414
|
+
c = false;
|
|
415
|
+
}
|
|
416
|
+
else if (number >= 1 && number < 10) {
|
|
417
|
+
p = 2;
|
|
418
|
+
d = 2;
|
|
419
|
+
r = true;
|
|
420
|
+
c = false;
|
|
421
|
+
f = 2;
|
|
422
|
+
}
|
|
423
|
+
else if (number >= 10 && number < 100) {
|
|
424
|
+
p = 1;
|
|
425
|
+
d = 1;
|
|
426
|
+
r = true;
|
|
427
|
+
c = false;
|
|
428
|
+
f = 1;
|
|
429
|
+
}
|
|
430
|
+
else if (number >= 100 && number < 1000) {
|
|
431
|
+
p = 0;
|
|
432
|
+
d = 0;
|
|
433
|
+
r = true;
|
|
434
|
+
c = false;
|
|
435
|
+
}
|
|
436
|
+
else if (number >= 1000) {
|
|
437
|
+
const x = Math.floor(Math.log10(number)) % 3;
|
|
438
|
+
p = 1 - x;
|
|
439
|
+
d = 1 - x;
|
|
440
|
+
r = true;
|
|
441
|
+
c = true;
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
p = 0;
|
|
445
|
+
d = 0;
|
|
446
|
+
r = true;
|
|
447
|
+
c = true;
|
|
448
|
+
f = 2;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
// precision === "high"
|
|
453
|
+
if (number >= 0 && number < 1) {
|
|
454
|
+
p = 33;
|
|
455
|
+
d = 4;
|
|
456
|
+
r = false;
|
|
457
|
+
c = false;
|
|
458
|
+
}
|
|
459
|
+
else if (number >= 1 && number < 10) {
|
|
460
|
+
p = 3;
|
|
461
|
+
d = 3;
|
|
462
|
+
r = true;
|
|
463
|
+
c = false;
|
|
464
|
+
}
|
|
465
|
+
else if (number >= 10 && number < 100) {
|
|
466
|
+
p = 2;
|
|
467
|
+
d = 2;
|
|
468
|
+
r = true;
|
|
469
|
+
c = false;
|
|
470
|
+
}
|
|
471
|
+
else if (number >= 100 && number < 1000) {
|
|
472
|
+
p = 2;
|
|
473
|
+
d = 2;
|
|
474
|
+
r = true;
|
|
475
|
+
c = false;
|
|
476
|
+
}
|
|
477
|
+
else if (number >= 1000 && number < 10000) {
|
|
478
|
+
p = 1;
|
|
479
|
+
d = 1;
|
|
480
|
+
r = true;
|
|
481
|
+
c = false;
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
p = 0;
|
|
485
|
+
d = 0;
|
|
486
|
+
r = true;
|
|
487
|
+
c = false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// For scientific notation, increase precision to ensure correct representation
|
|
491
|
+
if (this.isENotation(originalInput)) {
|
|
492
|
+
p = Math.max(p, 20);
|
|
493
|
+
r = false;
|
|
494
|
+
}
|
|
495
|
+
return this.reducePrecision(numberString, p, d, r, c, f, template, language, outputFormat, prefixMarker, postfixMarker, prefix, postfix, thousandSeparator, decimalSeparator, originalInput);
|
|
496
|
+
}
|
|
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 = '') {
|
|
498
|
+
var _a, _b;
|
|
499
|
+
if (numberString === undefined || numberString === null || numberString.trim() === '') {
|
|
500
|
+
return {};
|
|
501
|
+
}
|
|
502
|
+
// Handle negative zero
|
|
503
|
+
if (numberString === '-0' || numberString === '-0.0') {
|
|
504
|
+
numberString = numberString.substring(1); // Remove negative sign for zero
|
|
505
|
+
}
|
|
506
|
+
numberString = numberString.toString();
|
|
507
|
+
const maxPrecision = 30;
|
|
508
|
+
const maxIntegerDigits = 21;
|
|
509
|
+
const scaleUnits = template.match(/^(number|percent)$/g)
|
|
510
|
+
? {
|
|
511
|
+
'': '',
|
|
512
|
+
K: ' هزار',
|
|
513
|
+
M: ' میلیون',
|
|
514
|
+
B: ' میلیارد',
|
|
515
|
+
T: ' تریلیون',
|
|
516
|
+
Qd: ' کادریلیون',
|
|
517
|
+
Qt: ' کنتیلیون',
|
|
518
|
+
}
|
|
519
|
+
: {
|
|
520
|
+
'': '',
|
|
521
|
+
K: ' هزار ت',
|
|
522
|
+
M: ' میلیون ت',
|
|
523
|
+
B: ' میلیارد ت',
|
|
524
|
+
T: ' همت',
|
|
525
|
+
Qd: ' هزار همت',
|
|
526
|
+
Qt: ' میلیون همت',
|
|
527
|
+
};
|
|
528
|
+
const fullScaleUnits = template.match(/^(number|percent)$/g)
|
|
529
|
+
? {
|
|
530
|
+
'': '',
|
|
531
|
+
K: ' هزار',
|
|
532
|
+
M: ' میلیون',
|
|
533
|
+
B: ' میلیارد',
|
|
534
|
+
T: ' تریلیون',
|
|
535
|
+
Qd: ' کادریلیون',
|
|
536
|
+
Qt: ' کنتیلیون',
|
|
537
|
+
}
|
|
538
|
+
: {
|
|
539
|
+
'': '',
|
|
540
|
+
K: ' هزار تومان',
|
|
541
|
+
M: ' میلیون تومان',
|
|
542
|
+
B: ' میلیارد تومان',
|
|
543
|
+
T: ' هزار میلیارد تومان',
|
|
544
|
+
Qd: ' کادریلیون تومان',
|
|
545
|
+
Qt: ' کنتیلیون تومان',
|
|
546
|
+
};
|
|
547
|
+
let parts = /^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(numberString);
|
|
548
|
+
if (!parts) {
|
|
549
|
+
return {};
|
|
550
|
+
}
|
|
551
|
+
const sign = parts[1] || '';
|
|
552
|
+
let nonFractionalStr = parts[2];
|
|
553
|
+
let fractionalZeroStr = parts[3];
|
|
554
|
+
let fractionalNonZeroStr = parts[4];
|
|
555
|
+
let unitPrefix = '';
|
|
556
|
+
let unitPostfix = '';
|
|
557
|
+
if (fractionalZeroStr.length >= maxPrecision) {
|
|
558
|
+
// Number is smaller than maximum precision
|
|
559
|
+
fractionalZeroStr = '0'.padEnd(maxPrecision - 1, '0');
|
|
560
|
+
fractionalNonZeroStr = '1';
|
|
561
|
+
}
|
|
562
|
+
else if (fractionalZeroStr.length + nonZeroDigits > precision) {
|
|
563
|
+
// decrease non-zero digits
|
|
564
|
+
nonZeroDigits = precision - fractionalZeroStr.length;
|
|
565
|
+
if (nonZeroDigits < 1)
|
|
566
|
+
nonZeroDigits = 1;
|
|
567
|
+
}
|
|
568
|
+
else if (nonFractionalStr.length > maxIntegerDigits) {
|
|
569
|
+
nonFractionalStr = '0';
|
|
570
|
+
fractionalZeroStr = '';
|
|
571
|
+
fractionalNonZeroStr = '';
|
|
572
|
+
}
|
|
573
|
+
// compress large numbers
|
|
574
|
+
if (compress && nonFractionalStr.length >= 4) {
|
|
575
|
+
const scaleUnitKeys = Object.keys(scaleUnits);
|
|
576
|
+
let scaledWholeNumber = nonFractionalStr;
|
|
577
|
+
let unitIndex = 0;
|
|
578
|
+
while (+scaledWholeNumber > 999 && unitIndex < scaleUnitKeys.length - 1) {
|
|
579
|
+
scaledWholeNumber = (+scaledWholeNumber / 1000).toFixed(2);
|
|
580
|
+
unitIndex++;
|
|
581
|
+
}
|
|
582
|
+
unitPostfix = scaleUnitKeys[unitIndex];
|
|
583
|
+
parts = /^(-)?(\d+)\.?([0]*)(\d*)$/g.exec(scaledWholeNumber.toString());
|
|
584
|
+
if (!parts) {
|
|
585
|
+
return {};
|
|
586
|
+
}
|
|
587
|
+
// sign = parts[1] || "";
|
|
588
|
+
nonFractionalStr = parts[2];
|
|
589
|
+
fractionalZeroStr = parts[3];
|
|
590
|
+
fractionalNonZeroStr = parts[4];
|
|
591
|
+
}
|
|
592
|
+
// Truncate the fractional part or round it
|
|
593
|
+
// if (precision > 0 && nonZeroDigits > 0 && fractionalNonZeroStr.length > nonZeroDigits) {
|
|
594
|
+
if (fractionalNonZeroStr.length > nonZeroDigits) {
|
|
595
|
+
if (!round) {
|
|
596
|
+
fractionalNonZeroStr = fractionalNonZeroStr.substring(0, nonZeroDigits);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
if (parseInt(fractionalNonZeroStr[nonZeroDigits]) < 5) {
|
|
600
|
+
fractionalNonZeroStr = fractionalNonZeroStr.substring(0, nonZeroDigits);
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
fractionalNonZeroStr = (parseInt(fractionalNonZeroStr.substring(0, nonZeroDigits)) + 1).toString();
|
|
604
|
+
// If overflow occurs (e.g., 999 + 1 = 1000), adjust the substring length
|
|
605
|
+
if (fractionalNonZeroStr.length > nonZeroDigits) {
|
|
606
|
+
if (fractionalZeroStr.length > 0) {
|
|
607
|
+
fractionalZeroStr = fractionalZeroStr.substring(0, fractionalZeroStr.length - 1);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
nonFractionalStr = (Number(nonFractionalStr) + 1).toString();
|
|
611
|
+
fractionalNonZeroStr = fractionalNonZeroStr.substring(1);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
// Using dex style
|
|
618
|
+
if (compress && fractionalZeroStr !== '' && unitPostfix === '') {
|
|
619
|
+
fractionalZeroStr =
|
|
620
|
+
'0' +
|
|
621
|
+
fractionalZeroStr.length.toString().replace(/\d/g, function (match) {
|
|
622
|
+
return [
|
|
623
|
+
'₀',
|
|
624
|
+
'₁',
|
|
625
|
+
'₂',
|
|
626
|
+
'₃',
|
|
627
|
+
'₄',
|
|
628
|
+
'₅',
|
|
629
|
+
'₆',
|
|
630
|
+
'₇',
|
|
631
|
+
'₈',
|
|
632
|
+
'₉',
|
|
633
|
+
][parseInt(match, 10)];
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
// Check if the original input had trailing zeros
|
|
637
|
+
let fractionalPartStr = `${fractionalZeroStr}${fractionalNonZeroStr}`;
|
|
638
|
+
// Don't truncate trailing zeros when they're in the original string
|
|
639
|
+
if (fractionalPartStr.length > precision && !originalInput.includes('e')) {
|
|
640
|
+
fractionalPartStr = fractionalPartStr.substring(0, precision);
|
|
641
|
+
}
|
|
642
|
+
// For scientific notation and numbers with trailing zeros, preserve the format
|
|
643
|
+
if (originalInput.includes('e') || originalInput.includes('E')) {
|
|
644
|
+
// For scientific notation, use the converted string
|
|
645
|
+
}
|
|
646
|
+
else if (originalInput.includes('.')) {
|
|
647
|
+
// For regular numbers with decimal point, check for trailing zeros
|
|
648
|
+
const originalParts = originalInput.split('.');
|
|
649
|
+
if (originalParts.length === 2) {
|
|
650
|
+
const originalDecimal = originalParts[1];
|
|
651
|
+
// If original has more digits than what we have now, preserve those trailing zeros
|
|
652
|
+
if (originalDecimal.length > fractionalPartStr.length && originalDecimal.endsWith('0')) {
|
|
653
|
+
// Count trailing zeros in original
|
|
654
|
+
let trailingZeros = 0;
|
|
655
|
+
for (let i = originalDecimal.length - 1; i >= 0; i--) {
|
|
656
|
+
if (originalDecimal[i] === '0') {
|
|
657
|
+
trailingZeros++;
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
// Add back trailing zeros if they were in the original
|
|
664
|
+
if (trailingZeros > 0) {
|
|
665
|
+
fractionalPartStr = fractionalPartStr.padEnd(fractionalPartStr.length + trailingZeros, '0');
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
// Output Formating, Prefix, Postfix
|
|
671
|
+
if (template === 'usd') {
|
|
672
|
+
unitPrefix = language === 'en' ? '$' : '';
|
|
673
|
+
if (!unitPostfix)
|
|
674
|
+
unitPostfix = language === 'fa' ? ' دلار' : '';
|
|
675
|
+
}
|
|
676
|
+
else if (template === 'irr') {
|
|
677
|
+
if (!unitPostfix)
|
|
678
|
+
unitPostfix = language === 'fa' ? ' ر' : ' R';
|
|
679
|
+
}
|
|
680
|
+
else if (template === 'irt') {
|
|
681
|
+
if (!unitPostfix)
|
|
682
|
+
unitPostfix = language === 'fa' ? ' ت' : ' T';
|
|
683
|
+
}
|
|
684
|
+
else if (template === 'percent') {
|
|
685
|
+
if (language === 'en') {
|
|
686
|
+
unitPostfix += '%';
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
unitPostfix += !unitPostfix ? '٪' : ' درصد';
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
unitPrefix = prefix + unitPrefix;
|
|
693
|
+
unitPostfix += postfix;
|
|
694
|
+
if (outputFormat === 'html') {
|
|
695
|
+
if (unitPrefix)
|
|
696
|
+
unitPrefix = `<${prefixMarker}>${unitPrefix}</${prefixMarker}>`;
|
|
697
|
+
if (unitPostfix)
|
|
698
|
+
unitPostfix = `<${postfixMarker}>${unitPostfix}</${postfixMarker}>`;
|
|
699
|
+
}
|
|
700
|
+
else if (outputFormat === 'markdown') {
|
|
701
|
+
if (unitPrefix)
|
|
702
|
+
unitPrefix = `${prefixMarker}${unitPrefix}${prefixMarker}`;
|
|
703
|
+
if (unitPostfix)
|
|
704
|
+
unitPostfix = `${postfixMarker}${unitPostfix}${postfixMarker}`;
|
|
705
|
+
}
|
|
706
|
+
const thousandSeparatorRegex = /\B(?=(\d{3})+(?!\d))/g;
|
|
707
|
+
const fixedDecimalZeroStr = fixedDecimalZeros
|
|
708
|
+
? '.'.padEnd(fixedDecimalZeros + 1, '0')
|
|
709
|
+
: '';
|
|
710
|
+
let out = '';
|
|
711
|
+
let wholeNumberStr;
|
|
712
|
+
// FIXED: Changed condition to correctly handle numbers with trailing zeros
|
|
713
|
+
// Old condition: if (precision <= 0 || nonZeroDigits <= 0 || !fractionalNonZeroStr) {
|
|
714
|
+
// New condition checks if both fractional parts are empty
|
|
715
|
+
if (precision <= 0 || nonZeroDigits <= 0 || (fractionalNonZeroStr === '' && fractionalZeroStr === '')) {
|
|
716
|
+
wholeNumberStr = `${nonFractionalStr.replace(thousandSeparatorRegex, ',')}${fixedDecimalZeroStr}`;
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
wholeNumberStr = `${nonFractionalStr.replace(thousandSeparatorRegex, ',')}.${fractionalPartStr}`;
|
|
720
|
+
}
|
|
721
|
+
out = `${sign}${unitPrefix}${wholeNumberStr}${unitPostfix}`;
|
|
722
|
+
const formattedObject = {
|
|
723
|
+
value: out,
|
|
724
|
+
prefix: unitPrefix,
|
|
725
|
+
postfix: unitPostfix,
|
|
726
|
+
sign: sign,
|
|
727
|
+
wholeNumber: wholeNumberStr,
|
|
728
|
+
};
|
|
729
|
+
// replace custom config
|
|
730
|
+
formattedObject.value = ((_a = formattedObject === null || formattedObject === void 0 ? void 0 : formattedObject.value) !== null && _a !== void 0 ? _a : '')
|
|
731
|
+
.replace(/,/g, thousandSeparator)
|
|
732
|
+
.replace(/\./g, decimalSeparator);
|
|
733
|
+
// Convert output to Persian numerals if language is "fa"
|
|
734
|
+
if (language === 'fa') {
|
|
735
|
+
formattedObject.value = ((_b = formattedObject === null || formattedObject === void 0 ? void 0 : formattedObject.value) !== null && _b !== void 0 ? _b : '')
|
|
736
|
+
.replace(/[0-9]/g, c => String.fromCharCode(c.charCodeAt(0) + 1728))
|
|
737
|
+
.replace(/(K|M|B|T|Qt|Qd)/g, function (c) {
|
|
738
|
+
return String(scaleUnits[c]);
|
|
739
|
+
});
|
|
740
|
+
formattedObject.fullPostfix = unitPostfix
|
|
741
|
+
.replace(/[0-9]/g, c => String.fromCharCode(c.charCodeAt(0) + 1728))
|
|
742
|
+
.replace(/(K|M|B|T|Qt|Qd)/g, function (c) {
|
|
743
|
+
return String(fullScaleUnits[c]);
|
|
744
|
+
});
|
|
745
|
+
formattedObject.postfix = formattedObject.postfix
|
|
746
|
+
.replace(/[0-9]/g, c => String.fromCharCode(c.charCodeAt(0) + 1728))
|
|
747
|
+
.replace(/(K|M|B|T|Qt|Qd)/g, function (c) {
|
|
748
|
+
return String(scaleUnits[c]);
|
|
749
|
+
});
|
|
750
|
+
formattedObject.wholeNumber = formattedObject.wholeNumber
|
|
751
|
+
.replace(/[0-9]/g, c => String.fromCharCode(c.charCodeAt(0) + 1728))
|
|
752
|
+
.replace(/(K|M|B|T|Qt|Qd)/g, function (c) {
|
|
753
|
+
return String(scaleUnits[c]);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
return formattedObject;
|
|
757
|
+
}
|
|
758
|
+
_sanitizeLiveInput(input) {
|
|
759
|
+
var _a;
|
|
760
|
+
let sanitizedInput = input;
|
|
761
|
+
// Convert Farsi/Arabic numerals to Western numerals
|
|
762
|
+
sanitizedInput = sanitizedInput.replace(/[٠-٩۰-۹]/g, function (match) {
|
|
763
|
+
return String(match.charCodeAt(0) & 0xf);
|
|
764
|
+
});
|
|
765
|
+
// Handle potential E-notation
|
|
766
|
+
let stringForENotationProcessing = sanitizedInput;
|
|
767
|
+
let originalFarsiSeparator = null;
|
|
768
|
+
if (((_a = this.options.language) !== null && _a !== void 0 ? _a : 'en') === 'fa') {
|
|
769
|
+
const farsiDecimal = this.defaultLanguageConfig.fa.decimalSeparator || '٫'; // Default to '٫'
|
|
770
|
+
if (sanitizedInput.includes(farsiDecimal)) {
|
|
771
|
+
originalFarsiSeparator = farsiDecimal;
|
|
772
|
+
stringForENotationProcessing = sanitizedInput.replace(new RegExp(farsiDecimal, 'g'), '.');
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (this.isENotation(stringForENotationProcessing)) {
|
|
776
|
+
// Pass stringForENotationProcessing as the originalEString argument
|
|
777
|
+
const convertedValue = this.convertENotationToRegularNumber(Number(stringForENotationProcessing), stringForENotationProcessing);
|
|
778
|
+
// If we replaced a Farsi separator and the result has a '.', convert it back.
|
|
779
|
+
// Otherwise, use the converted value directly.
|
|
780
|
+
if (originalFarsiSeparator && convertedValue.includes('.')) {
|
|
781
|
+
sanitizedInput = convertedValue.replace(/\./g, originalFarsiSeparator);
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
sanitizedInput = convertedValue;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
// If not E-notation, sanitizedInput (with original separators after Farsi digit conversion) is returned.
|
|
788
|
+
return sanitizedInput;
|
|
789
|
+
}
|
|
790
|
+
_convertToFarsiDigits(value) {
|
|
791
|
+
if (this.options.language !== 'fa' || value === null || value === undefined) {
|
|
792
|
+
return value;
|
|
793
|
+
}
|
|
794
|
+
let farsiConvertedValue = "";
|
|
795
|
+
for (let i = 0; i < value.length; i++) {
|
|
796
|
+
const char = value[i];
|
|
797
|
+
if (char >= '0' && char <= '9') {
|
|
798
|
+
farsiConvertedValue += String.fromCharCode(char.charCodeAt(0) + 1728);
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
farsiConvertedValue += char;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return farsiConvertedValue;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
exports.default = NumberFormatter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NumberFormatter = void 0;
|
|
7
|
+
const format_1 = __importDefault(require("./format"));
|
|
8
|
+
exports.NumberFormatter = format_1.default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const format_1 = __importDefault(require("../../src/format")); // Adjust path if necessary based on actual structure
|
|
7
|
+
describe('NumberFormatter - Liveformat Template', () => {
|
|
8
|
+
const options = { template: 'liveformat' }; // Cast to any to allow new template type if TS is strict before build
|
|
9
|
+
const testCases = [
|
|
10
|
+
{ input: '0', expected: '0' },
|
|
11
|
+
{ input: '02', expected: '2' },
|
|
12
|
+
{ input: '29', expected: '29' },
|
|
13
|
+
{ input: '292', expected: '292' },
|
|
14
|
+
{ input: '2923', expected: '2,923' },
|
|
15
|
+
{ input: '29231', expected: '29,231' },
|
|
16
|
+
{ input: '292312', expected: '292,312' },
|
|
17
|
+
{ input: '2923123', expected: '2,923,123' },
|
|
18
|
+
{ input: '2923123.', expected: '2,923,123.' },
|
|
19
|
+
{ input: '2923123.0', expected: '2,923,123.0' },
|
|
20
|
+
{ input: '2923123.00', expected: '2,923,123.00' },
|
|
21
|
+
{ input: '2923123.003', expected: '2,923,123.003' },
|
|
22
|
+
{ input: '2923123.0030', expected: '2,923,123.0030' },
|
|
23
|
+
{ input: '2923123.00301', expected: '2,923,123.00301' },
|
|
24
|
+
{ input: '2923123.003012', expected: '2,923,123.003012' },
|
|
25
|
+
// Edge cases
|
|
26
|
+
{ input: '', expected: '' },
|
|
27
|
+
{ input: '.', expected: '0.' },
|
|
28
|
+
{ input: '0.0', expected: '0.0' },
|
|
29
|
+
{ input: '0.12', expected: '0.12' },
|
|
30
|
+
{ input: '.5', expected: '0.5' },
|
|
31
|
+
{ input: '007', expected: '7' },
|
|
32
|
+
{ input: '00.25', expected: '0.25' },
|
|
33
|
+
{ input: '12345.6700', expected: '12,345.6700' },
|
|
34
|
+
{ input: '000', expected: '0' },
|
|
35
|
+
{ input: '000.000', expected: '0.000' },
|
|
36
|
+
// Test cases for E-notation handling by incremental template
|
|
37
|
+
{ input: '1.23e-7', expected: '0.000000123' },
|
|
38
|
+
{ input: '1.23E-7', expected: '0.000000123' },
|
|
39
|
+
{ input: 0.000000123, expected: '0.000000123' },
|
|
40
|
+
{ input: '12345e-2', expected: '123.45' },
|
|
41
|
+
{ input: '0.00012345e+2', expected: '0.012345' },
|
|
42
|
+
{ input: '1.23e+8', expected: '123,000,000' },
|
|
43
|
+
{ input: '1.23E+8', expected: '123,000,000' },
|
|
44
|
+
{ input: 123000000, expected: '123,000,000' },
|
|
45
|
+
{ input: '1234567.89e+3', expected: '1,234,567,890' },
|
|
46
|
+
{ input: '123456789012345', expected: '123,456,789,012,345' },
|
|
47
|
+
{ input: 123456789012345, expected: '123,456,789,012,345' },
|
|
48
|
+
{ input: '1e-3', expected: '0.001' },
|
|
49
|
+
{ input: 0.001, expected: '0.001' },
|
|
50
|
+
{ input: '1e+3', expected: '1,000' },
|
|
51
|
+
{ input: 1000, expected: '1,000' },
|
|
52
|
+
{ input: '0e0', expected: '0' },
|
|
53
|
+
{ input: 0e0, expected: '0' },
|
|
54
|
+
{ input: '0.0e0', expected: '0.0' },
|
|
55
|
+
// Cases that might stress the convertENotationToRegularNumber
|
|
56
|
+
{ input: '123456789123456789123', expected: '123,456,789,123,456,789,123' },
|
|
57
|
+
{ input: 123456789123456789123, expected: '123,456,789,123,456,800,000' },
|
|
58
|
+
{ input: '1.0e-20', expected: '0.00000000000000000001' },
|
|
59
|
+
{ input: 1.0e-20, expected: '0.00000000000000000001' },
|
|
60
|
+
{ input: '1.0e+20', expected: '100,000,000,000,000,000,000' },
|
|
61
|
+
{ input: 1.0e+20, expected: '100,000,000,000,000,000,000' },
|
|
62
|
+
// Check inputs that convertENotationToRegularNumber might have previously struggled with
|
|
63
|
+
{ input: '1e21', expected: '1,000,000,000,000,000,000,000' },
|
|
64
|
+
{ input: 1e21, expected: '1,000,000,000,000,000,000,000' },
|
|
65
|
+
// Test with negative numbers in E-notation
|
|
66
|
+
{ input: '-1.23e-7', expected: '-0.000000123' },
|
|
67
|
+
{ input: -0.000000123, expected: '-0.000000123' },
|
|
68
|
+
{ input: '-1.23e+8', expected: '-123,000,000' },
|
|
69
|
+
{ input: -123000000, expected: '-123,000,000' },
|
|
70
|
+
];
|
|
71
|
+
testCases.forEach(({ input, expected }) => {
|
|
72
|
+
it(`should format '${input}' to '${expected}'`, () => {
|
|
73
|
+
const formatter = new format_1.default(options);
|
|
74
|
+
expect(formatter.toPlainString(input)).toBe(expected);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
// Test with different separators if possible, though the problem implies fixed separators for this template
|
|
78
|
+
// For now, assuming default separators (',' and '.') as per examples
|
|
79
|
+
describe('NumberFormatter - Liveformat Template with Fa Language (different separators)', () => {
|
|
80
|
+
const faOptions = {
|
|
81
|
+
template: 'liveformat',
|
|
82
|
+
language: 'fa' // 'fa' uses '٫' and '٬'
|
|
83
|
+
};
|
|
84
|
+
const formatter = new format_1.default(); // Create once
|
|
85
|
+
formatter.setLanguage('fa'); // Set language
|
|
86
|
+
formatter.setTemplate('liveformat', 'auto'); // Set template
|
|
87
|
+
// Confirmed Farsi Separators: Thousands: ٬ (U+066C), Decimal: ٫ (U+066B)
|
|
88
|
+
// Output Digits: Persian Numerals ۰۱۲... (U+06F0 range) as per TS implementation
|
|
89
|
+
it('should format Farsi input "۱۲۳۴" to "۱٬۲۳۴"', () => {
|
|
90
|
+
expect(formatter.toPlainString('۱۲۳۴')).toBe('۱٬۲۳۴');
|
|
91
|
+
});
|
|
92
|
+
it('should format Farsi input "۱۲۳۴۵٫۶۷" (Farsi decimal) to "۱۲٬۳۴۵٫۶۷"', () => {
|
|
93
|
+
expect(formatter.toPlainString('۱۲۳۴۵٫۶۷')).toBe('۱۲٬۳۴۵٫۶۷');
|
|
94
|
+
});
|
|
95
|
+
it('should format Farsi input "-۷۸۹٫۰۱" (Farsi decimal) to "-۷۸۹٫۰۱"', () => {
|
|
96
|
+
expect(formatter.toPlainString('-۷۸۹٫۰۱')).toBe('-۷۸۹٫۰۱');
|
|
97
|
+
});
|
|
98
|
+
it('should format Farsi input "۰" to "۰"', () => {
|
|
99
|
+
expect(formatter.toPlainString('۰')).toBe('۰');
|
|
100
|
+
});
|
|
101
|
+
it('should format Farsi input "-۰" to "۰"', () => {
|
|
102
|
+
expect(formatter.toPlainString('-۰')).toBe('۰');
|
|
103
|
+
});
|
|
104
|
+
it('should format Farsi input "۰٫۱۲" (Farsi decimal) to "۰٫۱۲"', () => {
|
|
105
|
+
expect(formatter.toPlainString('۰٫۱۲')).toBe('۰٫۱۲');
|
|
106
|
+
});
|
|
107
|
+
it('should format Farsi input "-۰٫۱۲" (Farsi decimal) to "-۰٫۱۲"', () => {
|
|
108
|
+
expect(formatter.toPlainString('-۰٫۱۲')).toBe('-۰٫۱۲');
|
|
109
|
+
});
|
|
110
|
+
it('should format Farsi input "۱۲۳۴۵۶۷۸۹٫۱۲۳" (Farsi decimal) to "۱۲۳٬۴۵۶٬۷۸۹٫۱۲۳"', () => {
|
|
111
|
+
expect(formatter.toPlainString('۱۲۳۴۵۶۷۸۹٫۱۲۳')).toBe('۱۲۳٬۴۵۶٬۷۸۹٫۱۲۳');
|
|
112
|
+
});
|
|
113
|
+
it('should format mixed Farsi/Western digit input "1۲۳۴٫۵۶" (Farsi decimal) to "۱٬۲۳۴٫۵۶"', () => {
|
|
114
|
+
expect(formatter.toPlainString('1۲۳۴٫۵۶')).toBe('۱٬۲۳۴٫۵۶');
|
|
115
|
+
});
|
|
116
|
+
it('should format Farsi input with only Farsi decimal separator "٫" to "۰٫"', () => {
|
|
117
|
+
expect(formatter.toPlainString('٫')).toBe('۰٫');
|
|
118
|
+
});
|
|
119
|
+
// User's critical case 1 (Farsi input, Farsi decimal '٫')
|
|
120
|
+
it('should format Farsi input "۹۳۱۲۸۳٫۰۰۰۹۱۲۳" to "۹۳۱٬۲۸۳٫۰۰۰۹۱۲۳"', () => {
|
|
121
|
+
expect(formatter.toPlainString('۹۳۱۲۸۳٫۰۰۰۹۱۲۳')).toBe('۹۳۱٬۲۸۳٫۰۰۰۹۱۲۳');
|
|
122
|
+
});
|
|
123
|
+
// User's critical case 2 (Western input "39312312.123123", lang 'fa')
|
|
124
|
+
// TS liveformat needs input to use '٫' if lang=fa for correct parsing.
|
|
125
|
+
it('should format Western digit input "39312312٫123123" (Farsi decimal) to "۳۹٬۳۱۲٬۳۱۲٫۱۲۳۱۲۳" when lang=fa', () => {
|
|
126
|
+
expect(formatter.toPlainString('39312312٫123123')).toBe('۳۹٬۳۱۲٬۳۱۲٫۱۲۳۱۲۳');
|
|
127
|
+
});
|
|
128
|
+
it('should format Farsi input "۱۲۳۴۵۶۷" to "۱٬۲۳۴٬۵۶۷"', () => {
|
|
129
|
+
expect(formatter.toPlainString('۱۲۳۴۵۶۷')).toBe('۱٬۲۳۴٬۵۶۷');
|
|
130
|
+
});
|
|
131
|
+
it('should format Farsi input "۱۲۳٫۴۵" (Farsi decimal) to "۱۲۳٫۴۵"', () => {
|
|
132
|
+
expect(formatter.toPlainString('۱۲۳٫۴۵')).toBe('۱۲۳٫۴۵');
|
|
133
|
+
});
|
|
134
|
+
// Test "0.0" like inputs
|
|
135
|
+
it('should format Farsi "۰٫۰" to "۰٫۰"', () => {
|
|
136
|
+
expect(formatter.toPlainString('۰٫۰')).toBe('۰٫۰');
|
|
137
|
+
});
|
|
138
|
+
it('should format Farsi "-۰٫۰" to "-۰٫۰"', () => {
|
|
139
|
+
expect(formatter.toPlainString('-۰٫۰')).toBe('-۰٫۰');
|
|
140
|
+
});
|
|
141
|
+
// Expectations for 0.0e0 related inputs, assuming they should result in "۰٫۰" after fixes
|
|
142
|
+
it('should format "0.0e0" to "۰٫۰" when lang=fa', () => {
|
|
143
|
+
expect(formatter.toPlainString('0.0e0')).toBe('۰٫۰');
|
|
144
|
+
});
|
|
145
|
+
it('should format "۰٫۰e0" (Farsi input) to "۰٫۰" when lang=fa', () => {
|
|
146
|
+
expect(formatter.toPlainString('۰٫۰e0')).toBe('۰٫۰');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|