11import { XNode , xmlValue } from "../../dom" ;
2- import { ExprContext } from "../expr-context" ;
2+ import { ExprContext } from "../../xslt/expr-context" ;
3+ import { XsltDecimalFormatSettings } from "../../xslt/xslt-decimal-format-settings" ;
34import { BooleanValue , NodeSetValue , NumberValue , StringValue } from "../values" ;
45import { assert , regExpEscape } from "./internal-functions" ;
56
@@ -66,7 +67,7 @@ export function current(context: ExprContext) {
6667}
6768
6869export function endsWith ( context : ExprContext ) {
69- assert ( this . args . length == 2 ) ;
70+ assert ( this . args . length === 2 ) ;
7071 const s0 = this . args [ 0 ] . evaluate ( context ) . stringValue ( ) ;
7172 const s1 = this . args [ 1 ] . evaluate ( context ) . stringValue ( ) ;
7273 const re = new RegExp ( `${ regExpEscape ( s1 ) } $` ) ;
@@ -78,6 +79,130 @@ export function _false() {
7879 return new BooleanValue ( false ) ;
7980}
8081
82+ /**
83+ * Formats the integer part of a number. Used by `formatNumber`.
84+ * @param _number The integer part of the number.
85+ * @param _mask The mask provided.
86+ * @returns The formatted integer part of the number as a string.
87+ */
88+ function formatNumberIntegerPart ( _number : string , _mask : string , settings : XsltDecimalFormatSettings ) : string {
89+ let formattedIntegerPart : string = "" ;
90+ let lastMaskPlaceholder : string = "" ;
91+ let numberPosition : number = _number . length - 1 ;
92+
93+ for ( let i = _mask . length - 1 ; i >= 0 ; i -- ) {
94+ lastMaskPlaceholder = _mask [ i ] ;
95+ switch ( lastMaskPlaceholder ) {
96+ case '#' :
97+ formattedIntegerPart = _number [ numberPosition ] + formattedIntegerPart ;
98+ numberPosition -- ;
99+ break ;
100+ case '0' :
101+ formattedIntegerPart = _number [ numberPosition ] + formattedIntegerPart ;
102+ numberPosition -- ;
103+ break ;
104+ case ',' :
105+ formattedIntegerPart = settings . groupingSeparator + formattedIntegerPart ;
106+ break ;
107+ }
108+ }
109+
110+ for ( ; numberPosition >= 0 ; numberPosition -- ) {
111+ formattedIntegerPart = _number [ numberPosition ] + formattedIntegerPart ;
112+ }
113+
114+ return formattedIntegerPart ;
115+ }
116+
117+ /**
118+ * Formats the decimal part of a number. Used by `formatNumber`.
119+ * @param _number The decimal part of the number.
120+ * @param _mask The mask provided.
121+ * @returns The formatted decimal part of the number as a string.
122+ */
123+ function formatNumberDecimalPart ( _number : string , _mask ?: string , settings ?: XsltDecimalFormatSettings ) : string {
124+ let formattedDecimalPart : string = "" ;
125+ // eslint-disable-next-line no-unused-vars
126+ let _settings : XsltDecimalFormatSettings = settings ;
127+ if ( _mask === null || _mask === undefined ) {
128+ return formattedDecimalPart ;
129+ }
130+
131+ let i = 0 ;
132+ for ( ; i < _mask . length ; i ++ ) {
133+ switch ( _mask [ i ] ) {
134+ case '#' :
135+ formattedDecimalPart += _number [ i ] ;
136+
137+ break ;
138+ case '0' :
139+ if ( i >= _number . length ) {
140+ formattedDecimalPart += '0' ;
141+ } else {
142+ formattedDecimalPart += _number [ i ] ;
143+ }
144+
145+ break ;
146+ }
147+ }
148+
149+ return formattedDecimalPart ;
150+ }
151+
152+ /**
153+ * XPath `format-number` function implementation.
154+ * @param context The Expression Context.
155+ * @returns A formatted number as string.
156+ */
157+ export function formatNumber ( context : ExprContext ) : StringValue {
158+ assert ( this . args . length >= 2 && this . args . length < 4 ) ;
159+ const firstArgument = this . args [ 0 ] . evaluate ( context ) . stringValue ( ) ;
160+ const secondArgument = this . args [ 1 ] . evaluate ( context ) . stringValue ( ) ;
161+ const numberTest = parseFloat ( firstArgument ) ;
162+ if ( isNaN ( numberTest ) ) {
163+ return new StringValue ( context . decimalFormatSettings . naN ) ;
164+ }
165+
166+ const numberParts = String ( numberTest ) . split ( '.' ) ;
167+ const maskParts = secondArgument . split ( '.' ) ;
168+ switch ( numberParts . length ) {
169+ case 1 : // Integer
170+ return new StringValue (
171+ formatNumberIntegerPart (
172+ numberParts [ 0 ] ,
173+ maskParts [ 0 ] ,
174+ context . decimalFormatSettings
175+ )
176+ ) ;
177+ case 2 : // Decimal
178+ const decimalPart = formatNumberDecimalPart (
179+ numberParts [ 1 ] ,
180+ maskParts . length === 2 ? maskParts [ 1 ] : undefined ,
181+ context . decimalFormatSettings
182+ ) ;
183+
184+ if ( decimalPart . length === 0 ) {
185+ return new StringValue (
186+ formatNumberIntegerPart (
187+ numberParts [ 0 ] ,
188+ maskParts [ 0 ] ,
189+ context . decimalFormatSettings
190+ )
191+ ) ;
192+ }
193+
194+ return new StringValue (
195+ formatNumberIntegerPart (
196+ numberParts [ 0 ] ,
197+ maskParts [ 0 ] ,
198+ context . decimalFormatSettings
199+ ) +
200+ context . decimalFormatSettings . decimalSeparator +
201+ decimalPart
202+ ) ;
203+ }
204+ }
205+
81206export function floor ( context : ExprContext ) {
82207 assert ( this . args . length === 1 ) ;
83208 const num = this . args [ 0 ] . evaluate ( context ) . numberValue ( ) ;
0 commit comments