@tradik/xslt3 0.0.0-stage → 1.0.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/LICENSE.md +28 -0
- package/README.md +98 -2
- package/package.json +43 -4
- package/src/errors.js +20 -0
- package/src/functions/aggregates.js +144 -0
- package/src/functions/arraySort.js +33 -0
- package/src/functions/arrays.js +181 -0
- package/src/functions/attributeTypes.js +58 -0
- package/src/functions/baseUris.js +82 -0
- package/src/functions/collations.js +127 -0
- package/src/functions/collections.js +84 -0
- package/src/functions/context.js +83 -0
- package/src/functions/core.js +167 -0
- package/src/functions/datetime.js +203 -0
- package/src/functions/deepEqual.js +132 -0
- package/src/functions/diagnostics.js +88 -0
- package/src/functions/focus.js +34 -0
- package/src/functions/format/dateComponents.js +176 -0
- package/src/functions/format/datePicture.js +115 -0
- package/src/functions/format/decimalFormat.js +65 -0
- package/src/functions/format/digitPattern.js +145 -0
- package/src/functions/format/formatDateTime.js +204 -0
- package/src/functions/format/formatInteger.js +109 -0
- package/src/functions/format/formatNumber.js +165 -0
- package/src/functions/format/fractionFormat.js +80 -0
- package/src/functions/format/germanWords.js +89 -0
- package/src/functions/format/numberLanguages.js +41 -0
- package/src/functions/format/numberPicture.js +202 -0
- package/src/functions/format/numberWords.js +155 -0
- package/src/functions/format/place.js +74 -0
- package/src/functions/format/timezoneFormat.js +89 -0
- package/src/functions/higherOrder.js +106 -0
- package/src/functions/ids.js +127 -0
- package/src/functions/json/index.js +17 -0
- package/src/functions/json/jsonChars.js +110 -0
- package/src/functions/json/jsonParser.js +203 -0
- package/src/functions/json/jsonStrings.js +116 -0
- package/src/functions/json/jsonToXml.js +116 -0
- package/src/functions/json/parseJson.js +119 -0
- package/src/functions/json/xmlRepresentation.js +103 -0
- package/src/functions/json/xmlToJson.js +147 -0
- package/src/functions/library.js +47 -0
- package/src/functions/mapArrayEqual.js +41 -0
- package/src/functions/maps.js +152 -0
- package/src/functions/math.js +73 -0
- package/src/functions/nodePaths.js +181 -0
- package/src/functions/nodes.js +199 -0
- package/src/functions/numeric.js +148 -0
- package/src/functions/options.js +58 -0
- package/src/functions/parseIetfDate.js +104 -0
- package/src/functions/parseXml.js +77 -0
- package/src/functions/qnames.js +68 -0
- package/src/functions/random.js +128 -0
- package/src/functions/regex/analyzeString.js +144 -0
- package/src/functions/regex/charClass.js +112 -0
- package/src/functions/regex/compile.js +81 -0
- package/src/functions/regex/escapes.js +109 -0
- package/src/functions/regex/parser.js +169 -0
- package/src/functions/regex/reader.js +37 -0
- package/src/functions/regex/regexFunctions.js +140 -0
- package/src/functions/regex/translate.js +102 -0
- package/src/functions/regex/unicodeBlocks.js +194 -0
- package/src/functions/registry.js +140 -0
- package/src/functions/sequences.js +173 -0
- package/src/functions/serializeFunction.js +53 -0
- package/src/functions/signatures.js +105 -0
- package/src/functions/sort.js +102 -0
- package/src/functions/stringMatch.js +45 -0
- package/src/functions/strings.js +194 -0
- package/src/functions/stringsCore.js +66 -0
- package/src/functions/support.js +106 -0
- package/src/functions/textDecoding.js +117 -0
- package/src/functions/ucaCollation.js +127 -0
- package/src/functions/unparsedText.js +71 -0
- package/src/functions/uri.js +166 -0
- package/src/functions/xqueryModule.js +24 -0
- package/src/index.js +38 -0
- package/src/items/array.js +84 -0
- package/src/items/function.js +45 -0
- package/src/items/map.js +120 -0
- package/src/items/mapKey.js +82 -0
- package/src/serialize/index.js +129 -0
- package/src/serialize/markup/content.js +74 -0
- package/src/serialize/markup/dialect.js +178 -0
- package/src/serialize/markup/htmlData.js +70 -0
- package/src/serialize/markup/namespaces.js +145 -0
- package/src/serialize/markup/prolog.js +91 -0
- package/src/serialize/markup/writer.js +183 -0
- package/src/serialize/methods/adaptive.js +131 -0
- package/src/serialize/methods/json.js +158 -0
- package/src/serialize/methods/text.js +43 -0
- package/src/serialize/output/buffer.js +48 -0
- package/src/serialize/output/encoding.js +146 -0
- package/src/serialize/output/expander.js +204 -0
- package/src/serialize/params/fromElement.js +141 -0
- package/src/serialize/params/fromMap.js +128 -0
- package/src/serialize/params/names.js +69 -0
- package/src/serialize/params/settings.js +153 -0
- package/src/serialize/params/values.js +163 -0
- package/src/serialize/sequence.js +111 -0
- package/src/xdm/arithmetic.js +155 -0
- package/src/xdm/atomic.js +142 -0
- package/src/xdm/binary.js +92 -0
- package/src/xdm/calendar.js +71 -0
- package/src/xdm/cast.js +176 -0
- package/src/xdm/compare.js +192 -0
- package/src/xdm/datetime.js +206 -0
- package/src/xdm/decimal.js +200 -0
- package/src/xdm/duration.js +128 -0
- package/src/xdm/generalCompare.js +152 -0
- package/src/xdm/index.js +47 -0
- package/src/xdm/lexical.js +126 -0
- package/src/xdm/nodes.js +170 -0
- package/src/xdm/numeric.js +122 -0
- package/src/xdm/qname.js +65 -0
- package/src/xdm/strings.js +52 -0
- package/src/xdm/temporalArithmetic.js +180 -0
- package/src/xdm/timeline.js +68 -0
- package/src/xdm/types.js +178 -0
- package/src/xpath/eval/atomics.js +45 -0
- package/src/xpath/eval/axes.js +212 -0
- package/src/xpath/eval/calls.js +168 -0
- package/src/xpath/eval/coercion.js +146 -0
- package/src/xpath/eval/compiler.js +71 -0
- package/src/xpath/eval/constructors.js +129 -0
- package/src/xpath/eval/decimalFormats.js +93 -0
- package/src/xpath/eval/descendantMemo.js +77 -0
- package/src/xpath/eval/documentOrder.js +142 -0
- package/src/xpath/eval/domNodes.js +174 -0
- package/src/xpath/eval/dynamicCall.js +27 -0
- package/src/xpath/eval/dynamicContext.js +161 -0
- package/src/xpath/eval/flow.js +115 -0
- package/src/xpath/eval/functionItems.js +194 -0
- package/src/xpath/eval/groupedChildren.js +88 -0
- package/src/xpath/eval/inline.js +71 -0
- package/src/xpath/eval/namespaceNodes.js +90 -0
- package/src/xpath/eval/nodeTests.js +164 -0
- package/src/xpath/eval/operators.js +190 -0
- package/src/xpath/eval/paths.js +163 -0
- package/src/xpath/eval/positional.js +81 -0
- package/src/xpath/eval/predicates.js +56 -0
- package/src/xpath/eval/primary.js +99 -0
- package/src/xpath/eval/resources.js +149 -0
- package/src/xpath/eval/scope.js +108 -0
- package/src/xpath/eval/sequenceType.js +193 -0
- package/src/xpath/eval/staticContext.js +142 -0
- package/src/xpath/eval/steps.js +98 -0
- package/src/xpath/eval/subtype.js +154 -0
- package/src/xpath/eval/typeExprs.js +147 -0
- package/src/xpath/eval/typeNames.js +101 -0
- package/src/xpath/eval/uris.js +21 -0
- package/src/xpath/eval/values.js +64 -0
- package/src/xpath/index.js +217 -0
- package/src/xpath/syntax/ast.js +130 -0
- package/src/xpath/syntax/chars.js +58 -0
- package/src/xpath/syntax/constructors.js +144 -0
- package/src/xpath/syntax/expressions.js +107 -0
- package/src/xpath/syntax/index.js +44 -0
- package/src/xpath/syntax/kindTests.js +134 -0
- package/src/xpath/syntax/lexer.js +220 -0
- package/src/xpath/syntax/operators.js +111 -0
- package/src/xpath/syntax/paths.js +102 -0
- package/src/xpath/syntax/postfix.js +91 -0
- package/src/xpath/syntax/primaries.js +149 -0
- package/src/xpath/syntax/steps.js +146 -0
- package/src/xpath/syntax/syntaxError.js +31 -0
- package/src/xpath/syntax/tokenStream.js +163 -0
- package/src/xpath/syntax/typeAst.js +76 -0
- package/src/xpath/syntax/typeOperators.js +127 -0
- package/src/xpath/syntax/types.js +158 -0
- package/src/xslt/api.js +153 -0
- package/src/xslt/compiler/acceptance.js +178 -0
- package/src/xslt/compiler/accumulatorDecl.js +146 -0
- package/src/xslt/compiler/attributeValues.js +174 -0
- package/src/xslt/compiler/attributes.js +211 -0
- package/src/xslt/compiler/avt.js +103 -0
- package/src/xslt/compiler/body.js +154 -0
- package/src/xslt/compiler/children.js +105 -0
- package/src/xslt/compiler/compatibility.js +130 -0
- package/src/xslt/compiler/components.js +179 -0
- package/src/xslt/compiler/contextItem.js +95 -0
- package/src/xslt/compiler/decimalFormats.js +132 -0
- package/src/xslt/compiler/declarationKinds.js +82 -0
- package/src/xslt/compiler/elementInfo.js +246 -0
- package/src/xslt/compiler/expressions.js +175 -0
- package/src/xslt/compiler/globalContextItem.js +92 -0
- package/src/xslt/compiler/globals.js +209 -0
- package/src/xslt/compiler/linker.js +134 -0
- package/src/xslt/compiler/miscDecl.js +171 -0
- package/src/xslt/compiler/modeDecl.js +88 -0
- package/src/xslt/compiler/moduleLoading.js +111 -0
- package/src/xslt/compiler/modules.js +158 -0
- package/src/xslt/compiler/outputDecl.js +209 -0
- package/src/xslt/compiler/overrideLinks.js +141 -0
- package/src/xslt/compiler/packageChecks.js +84 -0
- package/src/xslt/compiler/packageRegistry.js +95 -0
- package/src/xslt/compiler/packageTable.js +181 -0
- package/src/xslt/compiler/packageVersions.js +143 -0
- package/src/xslt/compiler/packages.js +138 -0
- package/src/xslt/compiler/registry.js +234 -0
- package/src/xslt/compiler/staticStage.js +197 -0
- package/src/xslt/compiler/staticVariables.js +93 -0
- package/src/xslt/compiler/stylesheet.js +205 -0
- package/src/xslt/compiler/tailPosition.js +74 -0
- package/src/xslt/compiler/templatesDecl.js +180 -0
- package/src/xslt/compiler/useAccumulators.js +76 -0
- package/src/xslt/compiler/useWhen.js +94 -0
- package/src/xslt/compiler/visibility.js +160 -0
- package/src/xslt/index.js +11 -0
- package/src/xslt/instructions/analyzeString.js +137 -0
- package/src/xslt/instructions/assert.js +72 -0
- package/src/xslt/instructions/attributeSets.js +115 -0
- package/src/xslt/instructions/conditional.js +99 -0
- package/src/xslt/instructions/control.js +123 -0
- package/src/xslt/instructions/copy.js +151 -0
- package/src/xslt/instructions/elements.js +161 -0
- package/src/xslt/instructions/evaluate.js +189 -0
- package/src/xslt/instructions/fork.js +35 -0
- package/src/xslt/instructions/grouping.js +106 -0
- package/src/xslt/instructions/index.js +94 -0
- package/src/xslt/instructions/iterate.js +182 -0
- package/src/xslt/instructions/literal.js +142 -0
- package/src/xslt/instructions/maps.js +72 -0
- package/src/xslt/instructions/merge.js +190 -0
- package/src/xslt/instructions/number.js +205 -0
- package/src/xslt/instructions/output.js +102 -0
- package/src/xslt/instructions/params.js +110 -0
- package/src/xslt/instructions/resultDocument.js +143 -0
- package/src/xslt/instructions/sort.js +114 -0
- package/src/xslt/instructions/sourceDocument.js +73 -0
- package/src/xslt/instructions/templates.js +153 -0
- package/src/xslt/instructions/text.js +151 -0
- package/src/xslt/instructions/try.js +186 -0
- package/src/xslt/instructions/variables.js +119 -0
- package/src/xslt/names.js +204 -0
- package/src/xslt/patterns/axisOrigins.js +121 -0
- package/src/xslt/patterns/compile.js +151 -0
- package/src/xslt/patterns/general.js +132 -0
- package/src/xslt/patterns/grammar.js +190 -0
- package/src/xslt/patterns/modes.js +153 -0
- package/src/xslt/patterns/paths.js +130 -0
- package/src/xslt/patterns/patternChecks.js +29 -0
- package/src/xslt/patterns/steps.js +162 -0
- package/src/xslt/processor.js +178 -0
- package/src/xslt/runtime/accumulatorFunctions.js +48 -0
- package/src/xslt/runtime/accumulators.js +174 -0
- package/src/xslt/runtime/apply.js +153 -0
- package/src/xslt/runtime/atomicKeys.js +36 -0
- package/src/xslt/runtime/availability.js +162 -0
- package/src/xslt/runtime/baseUri.js +64 -0
- package/src/xslt/runtime/compilerHook.js +27 -0
- package/src/xslt/runtime/conditional.js +110 -0
- package/src/xslt/runtime/context.js +106 -0
- package/src/xslt/runtime/copy.js +123 -0
- package/src/xslt/runtime/copyFunctions.js +71 -0
- package/src/xslt/runtime/copyOrigins.js +60 -0
- package/src/xslt/runtime/defaultMethod.js +42 -0
- package/src/xslt/runtime/documentContent.js +93 -0
- package/src/xslt/runtime/dynamicXPath.js +90 -0
- package/src/xslt/runtime/functionCache.js +65 -0
- package/src/xslt/runtime/functionCall.js +57 -0
- package/src/xslt/runtime/functions.js +191 -0
- package/src/xslt/runtime/groups.js +108 -0
- package/src/xslt/runtime/invocation.js +156 -0
- package/src/xslt/runtime/iteration.js +107 -0
- package/src/xslt/runtime/keys.js +141 -0
- package/src/xslt/runtime/lazy.js +97 -0
- package/src/xslt/runtime/locations.js +86 -0
- package/src/xslt/runtime/machine.js +137 -0
- package/src/xslt/runtime/merging.js +167 -0
- package/src/xslt/runtime/namespaceScope.js +141 -0
- package/src/xslt/runtime/numberFormat.js +89 -0
- package/src/xslt/runtime/numbering.js +168 -0
- package/src/xslt/runtime/params.js +59 -0
- package/src/xslt/runtime/properties.js +103 -0
- package/src/xslt/runtime/rawResults.js +82 -0
- package/src/xslt/runtime/sequenceReceiver.js +102 -0
- package/src/xslt/runtime/snapshot.js +66 -0
- package/src/xslt/runtime/sortKeys.js +119 -0
- package/src/xslt/runtime/streamAvailable.js +60 -0
- package/src/xslt/runtime/strip.js +102 -0
- package/src/xslt/runtime/transformFunction.js +169 -0
- package/src/xslt/runtime/transformation.js +168 -0
- package/src/xslt/runtime/transformationContext.js +85 -0
- package/src/xslt/runtime/treeReceiver.js +194 -0
- package/src/xslt/runtime/values.js +92 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions on durations, dates and times (F&O 3.1 sections 8.3, 9.3 to
|
|
3
|
+
* 9.5, and the context functions of 16.1): component extraction,
|
|
4
|
+
* fn:dateTime, timezone adjustment, current-dateTime/date/time and
|
|
5
|
+
* implicit-timezone.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/functions/datetime
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XPathError } from "../errors.js";
|
|
11
|
+
import { AtomicValue } from "../xdm/atomic.js";
|
|
12
|
+
import { componentsOf, DateTimeValue } from "../xdm/datetime.js";
|
|
13
|
+
import { Decimal } from "../xdm/decimal.js";
|
|
14
|
+
import { DurationValue } from "../xdm/duration.js";
|
|
15
|
+
import { fromLocalSeconds, localSeconds } from "../xdm/timeline.js";
|
|
16
|
+
import { types } from "../xdm/types.js";
|
|
17
|
+
import { decimalItem, define, integerItem } from "./support.js";
|
|
18
|
+
|
|
19
|
+
/** @param {number} minutes @returns {AtomicValue} an xs:dayTimeDuration */
|
|
20
|
+
export const timezoneDuration = (minutes) =>
|
|
21
|
+
new AtomicValue(
|
|
22
|
+
types.dayTimeDuration,
|
|
23
|
+
new DurationValue(0, Decimal.of(BigInt(minutes * 60))),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Replaces the timezone of a date/time value, adjusting the local value
|
|
28
|
+
* when it had one (F&O 3.1 section 9.5).
|
|
29
|
+
* @param {AtomicValue} item - xs:dateTime, xs:date or xs:time
|
|
30
|
+
* @param {number|null} timezone - Minutes, null to remove the timezone
|
|
31
|
+
* @returns {AtomicValue}
|
|
32
|
+
*/
|
|
33
|
+
export function adjustToTimezone(item, timezone) {
|
|
34
|
+
const kind = item.type.primitive.localName;
|
|
35
|
+
const { value } = item;
|
|
36
|
+
let local = value;
|
|
37
|
+
if (value.timezone !== null && timezone !== null) {
|
|
38
|
+
const shift = Decimal.of(BigInt((timezone - value.timezone) * 60));
|
|
39
|
+
local = fromLocalSeconds(localSeconds(value).add(shift), timezone);
|
|
40
|
+
}
|
|
41
|
+
const fields = { timezone };
|
|
42
|
+
for (const name of componentsOf(kind)) fields[name] = local[name];
|
|
43
|
+
return new AtomicValue(types[kind], new DateTimeValue(fields));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The timezone argument of the adjust functions in minutes.
|
|
48
|
+
* @param {Array<AtomicValue>|undefined} sequence - Undefined: the implicit
|
|
49
|
+
* timezone; empty: no timezone
|
|
50
|
+
* @param {object} context
|
|
51
|
+
* @returns {number|null}
|
|
52
|
+
* @throws {XPathError} FODT0003 for an invalid timezone
|
|
53
|
+
*/
|
|
54
|
+
function timezoneArg(sequence, context) {
|
|
55
|
+
if (sequence === undefined) return context.implicitTimezone ?? 0;
|
|
56
|
+
if (sequence.length === 0) return null;
|
|
57
|
+
const seconds = sequence[0].value.seconds;
|
|
58
|
+
const minutes = seconds.div(Decimal.of(60n));
|
|
59
|
+
if (!minutes.isInteger() || Math.abs(minutes.toNumber()) > 14 * 60) {
|
|
60
|
+
throw new XPathError("FODT0003", `Invalid timezone ${seconds}S`);
|
|
61
|
+
}
|
|
62
|
+
return minutes.toNumber();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Declares a function of one optional argument returning one optional
|
|
67
|
+
* value.
|
|
68
|
+
* @param {string} local
|
|
69
|
+
* @param {string} param
|
|
70
|
+
* @param {string} returns
|
|
71
|
+
* @param {(value: *) => AtomicValue|null} extract
|
|
72
|
+
* @returns {import("./support.js").FunctionDefinition}
|
|
73
|
+
*/
|
|
74
|
+
const accessor = (local, param, returns, extract) =>
|
|
75
|
+
define(local, [`${param}?`], `${returns}?`, ([arg]) => {
|
|
76
|
+
const result = arg.length === 0 ? null : extract(arg[0].value);
|
|
77
|
+
return result === null ? [] : [result];
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const component = (name) => (value) =>
|
|
81
|
+
name === "second" ? decimalItem(value.second) : integerItem(value[name]);
|
|
82
|
+
const timezoneOf = (value) =>
|
|
83
|
+
value.timezone === null ? null : timezoneDuration(value.timezone);
|
|
84
|
+
|
|
85
|
+
/** Component accessors by type: [type name, [function suffix, component]] */
|
|
86
|
+
const extractors = [
|
|
87
|
+
["dateTime", ["year", "month", "day", "hours", "minutes", "seconds"]],
|
|
88
|
+
["date", ["year", "month", "day"]],
|
|
89
|
+
["time", ["hours", "minutes", "seconds"]],
|
|
90
|
+
];
|
|
91
|
+
const componentNames = {
|
|
92
|
+
year: "year",
|
|
93
|
+
month: "month",
|
|
94
|
+
day: "day",
|
|
95
|
+
hours: "hour",
|
|
96
|
+
minutes: "minute",
|
|
97
|
+
seconds: "second",
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const DAY = 86400n;
|
|
101
|
+
/** Duration component extractors on (months, whole seconds, seconds). */
|
|
102
|
+
const durationParts = {
|
|
103
|
+
years: (d) => integerItem(Math.trunc(d.months / 12)),
|
|
104
|
+
months: (d) => integerItem(d.months % 12),
|
|
105
|
+
days: (d) => integerItem(d.seconds.trunc() / DAY),
|
|
106
|
+
hours: (d) => integerItem((d.seconds.trunc() / 3600n) % 24n),
|
|
107
|
+
minutes: (d) => integerItem((d.seconds.trunc() / 60n) % 60n),
|
|
108
|
+
seconds: (d) => decimalItem(d.seconds.mod(Decimal.of(60n))),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The current date/time from the context, with the implicit timezone when
|
|
113
|
+
* it has none, projected on a type.
|
|
114
|
+
* @param {object} context
|
|
115
|
+
* @param {string} kind - "dateTime", "date" or "time"
|
|
116
|
+
* @returns {AtomicValue}
|
|
117
|
+
*/
|
|
118
|
+
function current(context, kind) {
|
|
119
|
+
const now = context.currentDateTime;
|
|
120
|
+
const fields = { timezone: now.timezone ?? context.implicitTimezone ?? 0 };
|
|
121
|
+
for (const name of componentsOf(kind)) fields[name] = now[name];
|
|
122
|
+
const type = kind === "dateTime" ? types.dateTimeStamp : types[kind];
|
|
123
|
+
return new AtomicValue(type, new DateTimeValue(fields));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* fn:dateTime.
|
|
128
|
+
* @param {Array<AtomicValue>[]} args - [$date, $time]
|
|
129
|
+
* @returns {Array<AtomicValue>}
|
|
130
|
+
*/
|
|
131
|
+
function dateTime([date, time]) {
|
|
132
|
+
if (date.length === 0 || time.length === 0) return [];
|
|
133
|
+
const d = date[0].value;
|
|
134
|
+
const t = time[0].value;
|
|
135
|
+
if (d.timezone !== null && t.timezone !== null && d.timezone !== t.timezone) {
|
|
136
|
+
throw new XPathError("FORG0008", "The date and time timezones differ");
|
|
137
|
+
}
|
|
138
|
+
const value = new DateTimeValue({
|
|
139
|
+
...t,
|
|
140
|
+
year: d.year,
|
|
141
|
+
month: d.month,
|
|
142
|
+
day: d.day,
|
|
143
|
+
timezone: d.timezone ?? t.timezone,
|
|
144
|
+
});
|
|
145
|
+
return [new AtomicValue(types.dateTime, value)];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** @type {import("./support.js").FunctionDefinition[]} */
|
|
149
|
+
export const dateTimeFunctions = [
|
|
150
|
+
...extractors.flatMap(([type, parts]) => [
|
|
151
|
+
...parts.map((part) =>
|
|
152
|
+
accessor(
|
|
153
|
+
`${part}-from-${type}`,
|
|
154
|
+
`xs:${type}`,
|
|
155
|
+
part === "seconds" ? "xs:decimal" : "xs:integer",
|
|
156
|
+
component(componentNames[part]),
|
|
157
|
+
),
|
|
158
|
+
),
|
|
159
|
+
accessor(
|
|
160
|
+
`timezone-from-${type}`,
|
|
161
|
+
`xs:${type}`,
|
|
162
|
+
"xs:dayTimeDuration",
|
|
163
|
+
timezoneOf,
|
|
164
|
+
),
|
|
165
|
+
]),
|
|
166
|
+
...Object.entries(durationParts).map(([part, extract]) =>
|
|
167
|
+
accessor(
|
|
168
|
+
`${part}-from-duration`,
|
|
169
|
+
"xs:duration",
|
|
170
|
+
part === "seconds" ? "xs:decimal" : "xs:integer",
|
|
171
|
+
extract,
|
|
172
|
+
),
|
|
173
|
+
),
|
|
174
|
+
define("dateTime", ["xs:date?", "xs:time?"], "xs:dateTime?", dateTime),
|
|
175
|
+
...["dateTime", "date", "time"].flatMap((type) => {
|
|
176
|
+
const impl = ([arg, timezone], context) =>
|
|
177
|
+
arg.length === 0
|
|
178
|
+
? []
|
|
179
|
+
: [adjustToTimezone(arg[0], timezoneArg(timezone, context))];
|
|
180
|
+
const local = `adjust-${type}-to-timezone`;
|
|
181
|
+
return [
|
|
182
|
+
define(local, [`xs:${type}?`], `xs:${type}?`, impl),
|
|
183
|
+
define(
|
|
184
|
+
local,
|
|
185
|
+
[`xs:${type}?`, "xs:dayTimeDuration?"],
|
|
186
|
+
`xs:${type}?`,
|
|
187
|
+
impl,
|
|
188
|
+
),
|
|
189
|
+
];
|
|
190
|
+
}),
|
|
191
|
+
define("current-dateTime", [], "xs:dateTimeStamp", (_a, context) => [
|
|
192
|
+
current(context, "dateTime"),
|
|
193
|
+
]),
|
|
194
|
+
define("current-date", [], "xs:date", (_a, context) => [
|
|
195
|
+
current(context, "date"),
|
|
196
|
+
]),
|
|
197
|
+
define("current-time", [], "xs:time", (_a, context) => [
|
|
198
|
+
current(context, "time"),
|
|
199
|
+
]),
|
|
200
|
+
define("implicit-timezone", [], "xs:dayTimeDuration", (_a, context) => [
|
|
201
|
+
timezoneDuration(context.implicitTimezone ?? 0),
|
|
202
|
+
]),
|
|
203
|
+
];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fn:deep-equal (F&O 3.1 section 14.2.1) for atomic values and DOM nodes
|
|
3
|
+
* of any implementation (nodeType, namespaceURI, localName, nodeName,
|
|
4
|
+
* nodeValue, attributes and childNodes are read).
|
|
5
|
+
*
|
|
6
|
+
* Maps, arrays and function items are delegated to the hook
|
|
7
|
+
* `options.deepEqualItem(a, b, options)`, which the evaluator supplies
|
|
8
|
+
* through the dynamic context (`context.deepEqualItem`) because it owns
|
|
9
|
+
* their representation. Without the hook such items raise FOTY0015.
|
|
10
|
+
*
|
|
11
|
+
* @module @tradik/xslt3/functions/deepEqual
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { XPathError } from "../errors.js";
|
|
15
|
+
import { deepEqualAtomic } from "../xdm/compare.js";
|
|
16
|
+
import { isAtomic, isNode } from "../xdm/atomic.js";
|
|
17
|
+
import { nodeStringValue } from "../xdm/nodes.js";
|
|
18
|
+
|
|
19
|
+
const XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
|
|
20
|
+
const ELEMENT = 1;
|
|
21
|
+
const ATTRIBUTE = 2;
|
|
22
|
+
const PROCESSING_INSTRUCTION = 7;
|
|
23
|
+
const COMMENT = 8;
|
|
24
|
+
const DOCUMENT = 9;
|
|
25
|
+
const DOCUMENT_FRAGMENT = 11;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {object} DeepEqualOptions
|
|
29
|
+
* @property {(a: string, b: string) => number} collation
|
|
30
|
+
* @property {number} implicitTimezone
|
|
31
|
+
* @property {(a: *, b: *, options: DeepEqualOptions) => boolean} [deepEqualItem]
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** @param {Node} node @returns {string} "{uri}local" */
|
|
35
|
+
const expandedName = (node) =>
|
|
36
|
+
`{${node.namespaceURI ?? ""}}${node.localName ?? node.nodeName}`;
|
|
37
|
+
|
|
38
|
+
/** @param {Node} node @returns {Node[]} children without comments and PIs */
|
|
39
|
+
const significantChildren = (node) =>
|
|
40
|
+
Array.from(node.childNodes).filter(
|
|
41
|
+
(child) =>
|
|
42
|
+
child.nodeType !== COMMENT && child.nodeType !== PROCESSING_INSTRUCTION,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/** @param {Element} node @returns {Attr[]} attributes without namespace declarations */
|
|
46
|
+
const realAttributes = (node) =>
|
|
47
|
+
Array.from(node.attributes ?? []).filter(
|
|
48
|
+
(attr) => attr.namespaceURI !== XMLNS_NAMESPACE,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {Node} a
|
|
53
|
+
* @param {Node} b
|
|
54
|
+
* @param {DeepEqualOptions} options
|
|
55
|
+
* @returns {boolean} whether two nodes are deep-equal
|
|
56
|
+
*/
|
|
57
|
+
export function deepEqualNodes(a, b, options) {
|
|
58
|
+
const kind = (node) =>
|
|
59
|
+
node.nodeType === DOCUMENT_FRAGMENT ? DOCUMENT : node.nodeType;
|
|
60
|
+
if (kind(a) !== kind(b)) return false;
|
|
61
|
+
const sameText = () =>
|
|
62
|
+
options.collation(nodeStringValue(a), nodeStringValue(b)) === 0;
|
|
63
|
+
switch (kind(a)) {
|
|
64
|
+
case DOCUMENT:
|
|
65
|
+
return childrenEqual(a, b, options);
|
|
66
|
+
case ELEMENT: {
|
|
67
|
+
if (expandedName(a) !== expandedName(b)) return false;
|
|
68
|
+
const attributes = realAttributes(a);
|
|
69
|
+
const others = realAttributes(b);
|
|
70
|
+
return (
|
|
71
|
+
attributes.length === others.length &&
|
|
72
|
+
attributes.every((attr) =>
|
|
73
|
+
others.some((other) => deepEqualNodes(attr, other, options)),
|
|
74
|
+
) &&
|
|
75
|
+
childrenEqual(a, b, options)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
case ATTRIBUTE:
|
|
79
|
+
case PROCESSING_INSTRUCTION:
|
|
80
|
+
return expandedName(a) === expandedName(b) && sameText();
|
|
81
|
+
default:
|
|
82
|
+
// text, comment and namespace nodes
|
|
83
|
+
return sameText();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {Node} a
|
|
89
|
+
* @param {Node} b
|
|
90
|
+
* @param {DeepEqualOptions} options
|
|
91
|
+
* @returns {boolean} whether the significant children are pairwise equal
|
|
92
|
+
*/
|
|
93
|
+
function childrenEqual(a, b, options) {
|
|
94
|
+
const x = significantChildren(a);
|
|
95
|
+
const y = significantChildren(b);
|
|
96
|
+
return (
|
|
97
|
+
x.length === y.length &&
|
|
98
|
+
x.every((child, i) => deepEqualNodes(child, y[i], options))
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {*} a
|
|
104
|
+
* @param {*} b
|
|
105
|
+
* @param {DeepEqualOptions} options
|
|
106
|
+
* @returns {boolean} whether two items are deep-equal
|
|
107
|
+
* @throws {XPathError} FOTY0015 for function items without the hook
|
|
108
|
+
*/
|
|
109
|
+
function deepEqualItems(a, b, options) {
|
|
110
|
+
if (isAtomic(a) || isAtomic(b)) {
|
|
111
|
+
return isAtomic(a) && isAtomic(b) && deepEqualAtomic(a, b, options);
|
|
112
|
+
}
|
|
113
|
+
if (isNode(a) || isNode(b)) {
|
|
114
|
+
return isNode(a) && isNode(b) && deepEqualNodes(a, b, options);
|
|
115
|
+
}
|
|
116
|
+
if (options.deepEqualItem) return options.deepEqualItem(a, b, options);
|
|
117
|
+
throw new XPathError("FOTY0015", "deep-equal of function items");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether two sequences are deep-equal.
|
|
122
|
+
* @param {Array<*>} a
|
|
123
|
+
* @param {Array<*>} b
|
|
124
|
+
* @param {DeepEqualOptions} options
|
|
125
|
+
* @returns {boolean}
|
|
126
|
+
*/
|
|
127
|
+
export function deepEqualSequences(a, b, options) {
|
|
128
|
+
return (
|
|
129
|
+
a.length === b.length &&
|
|
130
|
+
a.every((item, i) => deepEqualItems(item, b[i], options))
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error and diagnostic functions (F&O 3.1 sections 3 and 4): fn:error and
|
|
3
|
+
* fn:trace, and the document functions fn:doc and fn:doc-available.
|
|
4
|
+
*
|
|
5
|
+
* @module @tradik/xslt3/functions/diagnostics
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { XPathError } from "../errors.js";
|
|
9
|
+
import { booleanItem } from "../xpath/eval/atomics.js";
|
|
10
|
+
|
|
11
|
+
/** Error code of an xs:QName: the local name in the err namespace. */
|
|
12
|
+
function errorCode(qname) {
|
|
13
|
+
if (qname.namespaceURI === "http://www.w3.org/2005/xqt-errors") {
|
|
14
|
+
return qname.localName;
|
|
15
|
+
}
|
|
16
|
+
return `Q{${qname.namespaceURI}}${qname.localName}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* fn:error.
|
|
21
|
+
* @param {Array<Array>} args
|
|
22
|
+
* @returns {never}
|
|
23
|
+
*/
|
|
24
|
+
function raise([code = [], description, object]) {
|
|
25
|
+
const qname = code[0]?.value;
|
|
26
|
+
const error = new XPathError(
|
|
27
|
+
qname ? errorCode(qname) : "FOER0000",
|
|
28
|
+
description?.[0]?.value ?? "Error raised by fn:error",
|
|
29
|
+
);
|
|
30
|
+
error.qname = qname;
|
|
31
|
+
error.errorObject = object;
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Function definitions. */
|
|
36
|
+
export const diagnosticFunctions = [
|
|
37
|
+
{ local: "error", params: [], returns: "item()*", impl: raise },
|
|
38
|
+
{ local: "error", params: ["xs:QName?"], returns: "item()*", impl: raise },
|
|
39
|
+
{
|
|
40
|
+
local: "error",
|
|
41
|
+
params: ["xs:QName?", "xs:string"],
|
|
42
|
+
returns: "item()*",
|
|
43
|
+
impl: raise,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
local: "error",
|
|
47
|
+
params: ["xs:QName?", "xs:string", "item()*"],
|
|
48
|
+
returns: "item()*",
|
|
49
|
+
impl: raise,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
local: "trace",
|
|
53
|
+
params: ["item()*"],
|
|
54
|
+
returns: "item()*",
|
|
55
|
+
impl: ([value], context) => (context.trace(value, ""), value),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
local: "trace",
|
|
59
|
+
params: ["item()*", "xs:string"],
|
|
60
|
+
returns: "item()*",
|
|
61
|
+
impl: ([value, [label]], context) => (
|
|
62
|
+
context.trace(value, label.value),
|
|
63
|
+
value
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
local: "doc",
|
|
68
|
+
params: ["xs:string?"],
|
|
69
|
+
returns: "document-node()?",
|
|
70
|
+
impl: ([uri], context) =>
|
|
71
|
+
uri.length ? [context.loadDocument(uri[0].value)] : [],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
local: "doc-available",
|
|
75
|
+
params: ["xs:string?"],
|
|
76
|
+
returns: "xs:boolean",
|
|
77
|
+
impl: ([uri], context) => {
|
|
78
|
+
if (!uri.length) return [booleanItem(false)];
|
|
79
|
+
try {
|
|
80
|
+
context.loadDocument(uri[0].value);
|
|
81
|
+
return [booleanItem(true)];
|
|
82
|
+
} catch {
|
|
83
|
+
// loadDocument raises only FODC0002 and FODC0005
|
|
84
|
+
return [booleanItem(false)];
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access to the focus from function implementations (definitions with
|
|
3
|
+
* `focus: true`).
|
|
4
|
+
*
|
|
5
|
+
* @module @tradik/xslt3/functions/focus
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { XPathError } from "../errors.js";
|
|
9
|
+
import { isNode } from "../xdm/atomic.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {import("./registry.js").DynamicContext} context
|
|
13
|
+
* @returns {*} the context item
|
|
14
|
+
* @throws {XPathError} XPDY0002 when the focus is absent
|
|
15
|
+
*/
|
|
16
|
+
export function focusItem(context) {
|
|
17
|
+
if (context.contextItem === undefined) {
|
|
18
|
+
throw new XPathError("XPDY0002", "The context item is absent");
|
|
19
|
+
}
|
|
20
|
+
return context.contextItem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {import("./registry.js").DynamicContext} context
|
|
25
|
+
* @returns {Node} the context item, which must be a node
|
|
26
|
+
* @throws {XPathError} XPDY0002 when absent, XPTY0004 when not a node
|
|
27
|
+
*/
|
|
28
|
+
export function focusNode(context) {
|
|
29
|
+
const item = focusItem(context);
|
|
30
|
+
if (!isNode(item)) {
|
|
31
|
+
throw new XPathError("XPTY0004", "The context item is not a node");
|
|
32
|
+
}
|
|
33
|
+
return item;
|
|
34
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Values and English names of the date/time components used by the
|
|
3
|
+
* format-dateTime family (F&O 3.1 section 9.8.4.1): day of year, ISO day
|
|
4
|
+
* of week and week numbers, 12-hour clock, names of months, days, am/pm
|
|
5
|
+
* and eras.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/functions/format/dateComponents
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { civilFromDays, daysFromCivil } from "../../xdm/calendar.js";
|
|
11
|
+
|
|
12
|
+
const MONTHS = (
|
|
13
|
+
"January February March April May June July August September October " +
|
|
14
|
+
"November December"
|
|
15
|
+
).split(" ");
|
|
16
|
+
const DAYS = "Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(
|
|
17
|
+
" ",
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Conventional English abbreviations longer than three letters, by name;
|
|
22
|
+
* every name of more than three letters also abbreviates to its first
|
|
23
|
+
* three.
|
|
24
|
+
*/
|
|
25
|
+
const ABBREVIATIONS = {
|
|
26
|
+
Tuesday: ["Tues"],
|
|
27
|
+
Wednesday: ["Weds"],
|
|
28
|
+
Thursday: ["Thur", "Thurs"],
|
|
29
|
+
September: ["Sept"],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The longest abbreviation of a name that fits a maximum width, else the
|
|
34
|
+
* name truncated to it.
|
|
35
|
+
* @param {string} name - Capitalized English name
|
|
36
|
+
* @param {number} maxWidth
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function abbreviate(name, maxWidth) {
|
|
40
|
+
const chars = Array.from(name);
|
|
41
|
+
if (chars.length <= maxWidth) return name;
|
|
42
|
+
const candidates = [name.slice(0, 3), ...(ABBREVIATIONS[name] ?? [])];
|
|
43
|
+
const fitting = candidates.filter(
|
|
44
|
+
(c) => c.length <= maxWidth && c.length < chars.length,
|
|
45
|
+
);
|
|
46
|
+
return fitting.length
|
|
47
|
+
? fitting[fitting.length - 1]
|
|
48
|
+
: chars.slice(0, maxWidth).join("");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Components available per value kind. */
|
|
52
|
+
const AVAILABLE = {
|
|
53
|
+
dateTime: "YMDdFWwHhPmsfZzCE",
|
|
54
|
+
date: "YMDdFWwZzCE",
|
|
55
|
+
time: "HhPmsfZzC",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} kind - "dateTime", "date" or "time"
|
|
60
|
+
* @param {string} component
|
|
61
|
+
* @returns {boolean} whether the component exists in values of the kind
|
|
62
|
+
*/
|
|
63
|
+
export const isAvailable = (kind, component) =>
|
|
64
|
+
AVAILABLE[kind].includes(component);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* ISO day of the week, 1 (Monday) to 7 (Sunday), of a day number.
|
|
68
|
+
* @param {number} days - Days since 1970-01-01 (a Thursday)
|
|
69
|
+
* @returns {number}
|
|
70
|
+
*/
|
|
71
|
+
const isoDayOfWeek = (days) => ((((days + 3) % 7) + 7) % 7) + 1;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Week numbers of a date: ISO week in year, and week in month where a
|
|
75
|
+
* Monday-to-Sunday week belongs to the month of its Thursday, except that
|
|
76
|
+
* the last days of a month whose Thursday is in the next month continue
|
|
77
|
+
* the count of their own month (as Saxon and the W3C tests do).
|
|
78
|
+
* @param {import("../../xdm/datetime.js").DateTimeValue} value
|
|
79
|
+
* @returns {{week: number, weekInMonth: number}}
|
|
80
|
+
*/
|
|
81
|
+
function weeks(value) {
|
|
82
|
+
const days = daysFromCivil(value.year, value.month, value.day);
|
|
83
|
+
const thursday = days - isoDayOfWeek(days) + 4;
|
|
84
|
+
const [year, , day] = civilFromDays(thursday);
|
|
85
|
+
const dayOfYear = thursday - daysFromCivil(year, 1, 1);
|
|
86
|
+
const monthStart = daysFromCivil(value.year, value.month, 1);
|
|
87
|
+
const dayInMonth = thursday >= monthStart ? thursday - monthStart + 1 : day;
|
|
88
|
+
return {
|
|
89
|
+
week: Math.floor(dayOfYear / 7) + 1,
|
|
90
|
+
weekInMonth: Math.floor((dayInMonth - 1) / 7) + 1,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The numeric value of a component (Y is the absolute year; s the whole
|
|
96
|
+
* seconds).
|
|
97
|
+
* @param {import("../../xdm/datetime.js").DateTimeValue} value
|
|
98
|
+
* @param {string} component - One of YMDdFWwHhms
|
|
99
|
+
* @returns {number}
|
|
100
|
+
*/
|
|
101
|
+
export function componentValue(value, component) {
|
|
102
|
+
switch (component) {
|
|
103
|
+
case "Y":
|
|
104
|
+
return Math.abs(value.year);
|
|
105
|
+
case "M":
|
|
106
|
+
return value.month;
|
|
107
|
+
case "D":
|
|
108
|
+
return value.day;
|
|
109
|
+
case "d":
|
|
110
|
+
return (
|
|
111
|
+
daysFromCivil(value.year, value.month, value.day) -
|
|
112
|
+
daysFromCivil(value.year, 1, 1) +
|
|
113
|
+
1
|
|
114
|
+
);
|
|
115
|
+
case "F":
|
|
116
|
+
return isoDayOfWeek(daysFromCivil(value.year, value.month, value.day));
|
|
117
|
+
case "W":
|
|
118
|
+
return weeks(value).week;
|
|
119
|
+
case "w":
|
|
120
|
+
return weeks(value).weekInMonth;
|
|
121
|
+
case "H":
|
|
122
|
+
return value.hour;
|
|
123
|
+
case "h":
|
|
124
|
+
return value.hour % 12 || 12;
|
|
125
|
+
case "m":
|
|
126
|
+
return value.minute;
|
|
127
|
+
default:
|
|
128
|
+
return Number(value.second.trunc());
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The English name of a component, or null when it has none.
|
|
134
|
+
* @param {import("../../xdm/datetime.js").DateTimeValue} value
|
|
135
|
+
* @param {string} component
|
|
136
|
+
* @param {string} calendar - Calendar used, e.g. "AD"
|
|
137
|
+
* @returns {string|null}
|
|
138
|
+
*/
|
|
139
|
+
export function componentName(value, component, calendar) {
|
|
140
|
+
switch (component) {
|
|
141
|
+
case "M":
|
|
142
|
+
return MONTHS[value.month - 1];
|
|
143
|
+
case "F":
|
|
144
|
+
return DAYS[componentValue(value, "F") - 1];
|
|
145
|
+
case "P":
|
|
146
|
+
return value.hour < 12 ? "am" : "pm";
|
|
147
|
+
case "C":
|
|
148
|
+
return calendar;
|
|
149
|
+
case "E":
|
|
150
|
+
if (calendar === "ISO") return value.year < 0 ? "-" : "";
|
|
151
|
+
return value.year > 0 ? "AD" : "BC";
|
|
152
|
+
default:
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Applies a name presentation modifier and the width modifier to a name.
|
|
159
|
+
* @param {string} name
|
|
160
|
+
* @param {string} token - "n", "N" or "Nn"
|
|
161
|
+
* @param {number|null} minWidth
|
|
162
|
+
* @param {number|null} maxWidth
|
|
163
|
+
* @returns {string}
|
|
164
|
+
*/
|
|
165
|
+
export function presentName(name, token, minWidth, maxWidth) {
|
|
166
|
+
const capitalized = name.charAt(0).toUpperCase() + name.slice(1);
|
|
167
|
+
const short =
|
|
168
|
+
maxWidth === null ? capitalized : abbreviate(capitalized, maxWidth);
|
|
169
|
+
const text =
|
|
170
|
+
token === "N"
|
|
171
|
+
? short.toUpperCase()
|
|
172
|
+
: token === "n"
|
|
173
|
+
? short.toLowerCase()
|
|
174
|
+
: short;
|
|
175
|
+
return minWidth === null ? text : text.padEnd(minWidth, " ");
|
|
176
|
+
}
|