@doenet/v06-to-v07 0.7.26-dev.569 → 0.7.26-dev.572
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/apply-assign-name-renames.d.ts +14 -0
- package/apply-assign-name-renames.d.ts.map +1 -0
- package/assign-names/apply-renames.d.ts +21 -0
- package/assign-names/apply-renames.d.ts.map +1 -0
- package/assign-names/break-into-pieces.d.ts +34 -0
- package/assign-names/break-into-pieces.d.ts.map +1 -0
- package/assign-names/composite-info.d.ts +61 -0
- package/assign-names/composite-info.d.ts.map +1 -0
- package/assign-names/context.d.ts +103 -0
- package/assign-names/context.d.ts.map +1 -0
- package/assign-names/prop-access-parts.d.ts +14 -0
- package/assign-names/prop-access-parts.d.ts.map +1 -0
- package/assign-names/register-assign-names.d.ts +38 -0
- package/assign-names/register-assign-names.d.ts.map +1 -0
- package/assign-names/register-composite.d.ts +26 -0
- package/assign-names/register-composite.d.ts.map +1 -0
- package/assign-names/rename-registry.d.ts +90 -0
- package/assign-names/rename-registry.d.ts.map +1 -0
- package/assign-names/visit-all.d.ts +35 -0
- package/assign-names/visit-all.d.ts.map +1 -0
- package/core-info/core.d.ts +1 -0
- package/core-info/core.d.ts.map +1 -1
- package/core-info/determine-prop-type.d.ts +9 -0
- package/core-info/determine-prop-type.d.ts.map +1 -1
- package/{index-BRvKEo9t.js → index-D-2i13ZI.js} +2839 -387
- package/{index-BRvKEo9t.js.map → index-D-2i13ZI.js.map} +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +1 -1
- package/normalize-capitalization.d.ts.map +1 -1
- package/package.json +1 -1
- package/{sha256-8sXzDnE0-oxoOCdy9-CqitCXCq.js → sha256-8sXzDnE0-hQwEEL17-Br5jiMaf.js} +2 -2
- package/{sha256-8sXzDnE0-oxoOCdy9-CqitCXCq.js.map → sha256-8sXzDnE0-hQwEEL17-Br5jiMaf.js.map} +1 -1
- package/slash-to-dot.d.ts.map +1 -1
- package/upgrade-assign-names.d.ts +32 -0
- package/upgrade-assign-names.d.ts.map +1 -0
- package/upgrade-attribute-syntax.d.ts +4 -1
- package/upgrade-attribute-syntax.d.ts.map +1 -1
- package/upgrade-collect-element.d.ts +4 -1
- package/upgrade-collect-element.d.ts.map +1 -1
- package/upgrade-copy-elements.d.ts +45 -0
- package/upgrade-copy-elements.d.ts.map +1 -0
- package/upgrade-copy-syntax.d.ts.map +1 -1
- package/upgrade-deprecated-attributes.d.ts +12 -0
- package/upgrade-deprecated-attributes.d.ts.map +1 -0
- package/upgrade-list-props.d.ts +4 -0
- package/upgrade-list-props.d.ts.map +1 -0
- package/upgrade-map-element.d.ts +4 -1
- package/upgrade-map-element.d.ts.map +1 -1
- package/upgrade-module-element.d.ts.map +1 -1
- package/utils.d.ts +34 -2
- package/utils.d.ts.map +1 -1
|
@@ -1849,16 +1849,16 @@ function findNodesWithPositionInfo(nodes) {
|
|
|
1849
1849
|
}
|
|
1850
1850
|
return ret;
|
|
1851
1851
|
}
|
|
1852
|
-
function macroToString(node) {
|
|
1852
|
+
function macroToString(node, forceParens = false) {
|
|
1853
1853
|
if (Array.isArray(node)) {
|
|
1854
|
-
return node
|
|
1854
|
+
return arrayToString(node);
|
|
1855
1855
|
}
|
|
1856
1856
|
switch (node.type) {
|
|
1857
1857
|
case "macro": {
|
|
1858
1858
|
const macro = unwrappedMacroToString$1(node);
|
|
1859
1859
|
let start = "$";
|
|
1860
1860
|
let end = "";
|
|
1861
|
-
if (macroNeedsParens$1(node)) {
|
|
1861
|
+
if (macroNeedsParens$1(node) || forceParens) {
|
|
1862
1862
|
start += "(";
|
|
1863
1863
|
end += ")";
|
|
1864
1864
|
}
|
|
@@ -1868,11 +1868,11 @@ function macroToString(node) {
|
|
|
1868
1868
|
const macro = unwrappedMacroToString$1(node.macro);
|
|
1869
1869
|
let start = "$$";
|
|
1870
1870
|
let end = "";
|
|
1871
|
-
if (macroNeedsParens$1(node)) {
|
|
1871
|
+
if (macroNeedsParens$1(node) || forceParens) {
|
|
1872
1872
|
start += "(";
|
|
1873
1873
|
end += ")";
|
|
1874
1874
|
}
|
|
1875
|
-
const args = node.input ? `(${node.input.map(macroToString).join(", ")})` : "";
|
|
1875
|
+
const args = node.input ? `(${node.input.map((arg) => macroToString(arg)).join(", ")})` : "";
|
|
1876
1876
|
return start + macro + end + args;
|
|
1877
1877
|
}
|
|
1878
1878
|
case "text":
|
|
@@ -1898,12 +1898,29 @@ function macroPathToString$1(path) {
|
|
|
1898
1898
|
function macroPathPartToString$1(pathPart) {
|
|
1899
1899
|
return pathPart.name + pathPart.index.map((part) => `[${macroToString(part.value)}]`).join("");
|
|
1900
1900
|
}
|
|
1901
|
+
function arrayToString(nodes) {
|
|
1902
|
+
const parts = nodes.map((n2) => macroToString(n2));
|
|
1903
|
+
let nextChar = "";
|
|
1904
|
+
for (let i2 = parts.length - 1; i2 >= 0; i2--) {
|
|
1905
|
+
const child = nodes[i2];
|
|
1906
|
+
if ((child.type === "macro" || child.type === "function") && isNameChar$1(nextChar) && isNameChar$1(parts[i2].slice(-1))) {
|
|
1907
|
+
parts[i2] = macroToString(child, true);
|
|
1908
|
+
}
|
|
1909
|
+
if (parts[i2]) {
|
|
1910
|
+
nextChar = parts[i2][0];
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
return parts.join("");
|
|
1914
|
+
}
|
|
1915
|
+
function isNameChar$1(char) {
|
|
1916
|
+
return /^[a-zA-Z0-9_]$/.test(char);
|
|
1917
|
+
}
|
|
1901
1918
|
function attrToString$1(attr) {
|
|
1902
1919
|
const name2 = attr.name;
|
|
1903
1920
|
if (attr.children.length === 0) {
|
|
1904
1921
|
return name2;
|
|
1905
1922
|
}
|
|
1906
|
-
const value = attr.children
|
|
1923
|
+
const value = arrayToString(attr.children);
|
|
1907
1924
|
return `${name2}=${quote(value)}`;
|
|
1908
1925
|
}
|
|
1909
1926
|
function macroNeedsParens$1(macro) {
|
|
@@ -1923,10 +1940,22 @@ function toXml(tree, options) {
|
|
|
1923
1940
|
const node = Array.isArray(tree) ? { type: "root", children: tree } : tree;
|
|
1924
1941
|
return nodesToXml(node, options);
|
|
1925
1942
|
}
|
|
1926
|
-
function nodesToXml(node, options) {
|
|
1943
|
+
function nodesToXml(node, options, forceParens = false) {
|
|
1927
1944
|
if (Array.isArray(node)) {
|
|
1928
1945
|
if (!node.some((n2) => n2.type === "pathPart")) {
|
|
1929
|
-
|
|
1946
|
+
const children = mergeAdjacentTextInArray(node);
|
|
1947
|
+
const parts = children.map((child) => nodesToXml(child, options));
|
|
1948
|
+
let nextChar = "";
|
|
1949
|
+
for (let i2 = parts.length - 1; i2 >= 0; i2--) {
|
|
1950
|
+
const child = children[i2];
|
|
1951
|
+
if ((child.type === "macro" || child.type === "function") && isNameChar(nextChar) && isNameChar(parts[i2].slice(-1))) {
|
|
1952
|
+
parts[i2] = nodesToXml(child, options, true);
|
|
1953
|
+
}
|
|
1954
|
+
if (parts[i2]) {
|
|
1955
|
+
nextChar = parts[i2][0];
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
return parts.join("");
|
|
1930
1959
|
} else {
|
|
1931
1960
|
return node.map((part) => nodesToXml(part, options)).join(".");
|
|
1932
1961
|
}
|
|
@@ -2011,12 +2040,12 @@ function nodesToXml(node, options) {
|
|
|
2011
2040
|
}
|
|
2012
2041
|
case "macro": {
|
|
2013
2042
|
if (node.version === "0.6") {
|
|
2014
|
-
return macroToString(node);
|
|
2043
|
+
return macroToString(node, forceParens);
|
|
2015
2044
|
}
|
|
2016
2045
|
const macro = unwrappedMacroToString(node, options);
|
|
2017
2046
|
let start = "$";
|
|
2018
2047
|
let end = "";
|
|
2019
|
-
if (macroNeedsParens(node)) {
|
|
2048
|
+
if (macroNeedsParens(node) || forceParens) {
|
|
2020
2049
|
start += "(";
|
|
2021
2050
|
end += ")";
|
|
2022
2051
|
}
|
|
@@ -2024,12 +2053,12 @@ function nodesToXml(node, options) {
|
|
|
2024
2053
|
}
|
|
2025
2054
|
case "function": {
|
|
2026
2055
|
if (node.version === "0.6") {
|
|
2027
|
-
return macroToString(node);
|
|
2056
|
+
return macroToString(node, forceParens);
|
|
2028
2057
|
}
|
|
2029
2058
|
const macro = unwrappedMacroToString(node, options);
|
|
2030
2059
|
let start = "$$";
|
|
2031
2060
|
let end = "";
|
|
2032
|
-
if (macroNeedsParens(node)) {
|
|
2061
|
+
if (macroNeedsParens(node) || forceParens) {
|
|
2033
2062
|
start += "(";
|
|
2034
2063
|
end += ")";
|
|
2035
2064
|
}
|
|
@@ -2082,6 +2111,9 @@ function attrToString(attr, options) {
|
|
|
2082
2111
|
function macroNeedsParens(macro) {
|
|
2083
2112
|
return macro.path.some((part) => part.name.includes("-"));
|
|
2084
2113
|
}
|
|
2114
|
+
function isNameChar(char) {
|
|
2115
|
+
return /^[a-zA-Z0-9_]$/.test(char);
|
|
2116
|
+
}
|
|
2085
2117
|
const StartTag = 1, StartCloseTag = 2, MissingCloseTag = 3, mismatchedStartCloseTag = 4, incompleteStartCloseTag = 5, commentContent$1 = 36, piContent$1 = 37, cdataContent$1 = 38, Text$1 = 7, Element = 12, EndTag = 13, OpenTag = 14, TagName = 15, Attribute = 16, AttributeValue = 19, CloseTag = 20, SelfCloseEndTag = 21, SelfClosingTag = 22, MismatchedCloseTag = 25;
|
|
2086
2118
|
const DefaultBufferLength = 1024;
|
|
2087
2119
|
let nextPropID = 0;
|
|
@@ -31765,7 +31797,7 @@ var init_before = __esmMin((() => {
|
|
|
31765
31797
|
})), once_default, init_once = __esmMin((() => {
|
|
31766
31798
|
init_partial(), init_before(), once_default = partial(before, 2);
|
|
31767
31799
|
}));
|
|
31768
|
-
function findKey(t4, n2, r2) {
|
|
31800
|
+
function findKey$2(t4, n2, r2) {
|
|
31769
31801
|
n2 = cb(n2, r2);
|
|
31770
31802
|
for (var i2 = keys$1(t4), a2, o2 = 0, s2 = i2.length; o2 < s2; o2++) if (a2 = i2[o2], n2(t4[a2], a2, t4)) return a2;
|
|
31771
31803
|
}
|
|
@@ -31815,7 +31847,7 @@ var init__createIndexFinder = __esmMin((() => {
|
|
|
31815
31847
|
init_findLastIndex(), init__createIndexFinder(), lastIndexOf_default = createIndexFinder(-1, findLastIndex_default);
|
|
31816
31848
|
}));
|
|
31817
31849
|
function find$1(t4, n2, r2) {
|
|
31818
|
-
var i2 = (_isArrayLike_default(t4) ? findIndex_default : findKey)(t4, n2, r2);
|
|
31850
|
+
var i2 = (_isArrayLike_default(t4) ? findIndex_default : findKey$2)(t4, n2, r2);
|
|
31819
31851
|
if (i2 !== void 0 && i2 !== -1) return t4[i2];
|
|
31820
31852
|
}
|
|
31821
31853
|
var init_find = __esmMin((() => {
|
|
@@ -32199,11 +32231,11 @@ var init_mixin = __esmMin((() => {
|
|
|
32199
32231
|
return t5 != null && (t5 = n2.apply(t5, arguments)), chainResult(this, t5);
|
|
32200
32232
|
};
|
|
32201
32233
|
}), underscore_array_methods_default = _$1$1;
|
|
32202
|
-
})), modules_exports = __exportAll({ VERSION: () => VERSION, after: () => after, all: () => every$1, allKeys: () => allKeys, any: () => some$1, assign: () => extendOwn_default, before: () => before, bind: () => bind_default, bindAll: () => bindAll_default, chain: () => chain, chunk: () => chunk, clone: () => clone, collect: () => map$2, compact: () => compact$1, compose: () => compose, constant: () => constant, contains: () => contains$1, countBy: () => countBy_default, create: () => create, debounce: () => debounce, default: () => underscore_array_methods_default, defaults: () => defaults_default, defer: () => defer_default, delay: () => delay_default, detect: () => find$1, difference: () => difference_default, drop: () => rest, each: () => each, escape: () => escape_default, every: () => every$1, extend: () => extend_default, extendOwn: () => extendOwn_default, filter: () => filter$2, find: () => find$1, findIndex: () => findIndex_default, findKey: () => findKey, findLastIndex: () => findLastIndex_default, findWhere: () => findWhere, first: () => first$2, flatten: () => flatten$2, foldl: () => reduce_default, foldr: () => reduceRight_default, forEach: () => each, functions: () => functions$1, get: () => get, groupBy: () => groupBy_default, has: () => has$2, head: () => first$2, identity: () => identity, include: () => contains$1, includes: () => contains$1, indexBy: () => indexBy_default, indexOf: () => indexOf_default, initial: () => initial, inject: () => reduce_default, intersection: () => intersection, invert: () => invert, invoke: () => invoke_default, isArguments: () => isArguments_default, isArray: () => isArray_default, isArrayBuffer: () => isArrayBuffer_default, isBoolean: () => isBoolean$2, isDataView: () => isDataView_default, isDate: () => isDate_default, isElement: () => isElement, isEmpty: () => isEmpty$1, isEqual: () => isEqual, isError: () => isError_default, isFinite: () => isFinite$1, isFunction: () => isFunction_default, isMap: () => isMap_default, isMatch: () => isMatch, isNaN: () => isNaN$1, isNull: () => isNull, isNumber: () => isNumber_default, isObject: () => isObject$2, isRegExp: () => isRegExp_default, isSet: () => isSet_default, isString: () => isString_default, isSymbol: () => isSymbol_default, isTypedArray: () => isTypedArray_default, isUndefined: () => isUndefined$2, isWeakMap: () => isWeakMap_default, isWeakSet: () => isWeakSet_default, iteratee: () => iteratee, keys: () => keys$1, last: () => last$1, lastIndexOf: () => lastIndexOf_default, map: () => map$2, mapObject: () => mapObject, matcher: () => matcher, matches: () => matcher, max: () => max$2, memoize: () => memoize$2, methods: () => functions$1, min: () => min$2, mixin: () => mixin, negate: () => negate, noop: () => noop, now: () => now_default, object: () => object, omit: () => omit_default, once: () => once_default, pairs: () => pairs, partial: () => partial, partition: () => partition_default, pick: () => pick_default, pluck: () => pluck, property: () => property, propertyOf: () => propertyOf, random: () => random, range: () => range, reduce: () => reduce_default, reduceRight: () => reduceRight_default, reject: () => reject$1, rest: () => rest, restArguments: () => restArguments, result: () => result, sample: () => sample, select: () => filter$2, shuffle: () => shuffle, size: () => size, some: () => some$1, sortBy: () => sortBy, sortedIndex: () => sortedIndex, tail: () => rest, take: () => first$2, tap: () => tap, template: () => template, templateSettings: () => templateSettings_default, throttle: () => throttle, times: () => times, toArray: () => toArray, toPath: () => toPath$1, transpose: () => unzip, unescape: () => unescape_default, union: () => union_default, uniq: () => uniq$1, unique: () => uniq$1, uniqueId: () => uniqueId, unzip: () => unzip, values: () => values$2, where: () => where, without: () => without_default, wrap: () => wrap, zip: () => zip_default }), init_modules = __esmMin((() => {
|
|
32234
|
+
})), modules_exports = __exportAll({ VERSION: () => VERSION, after: () => after, all: () => every$1, allKeys: () => allKeys, any: () => some$1, assign: () => extendOwn_default, before: () => before, bind: () => bind_default, bindAll: () => bindAll_default, chain: () => chain, chunk: () => chunk, clone: () => clone, collect: () => map$2, compact: () => compact$1, compose: () => compose, constant: () => constant, contains: () => contains$1, countBy: () => countBy_default, create: () => create, debounce: () => debounce, default: () => underscore_array_methods_default, defaults: () => defaults_default, defer: () => defer_default, delay: () => delay_default, detect: () => find$1, difference: () => difference_default, drop: () => rest, each: () => each, escape: () => escape_default, every: () => every$1, extend: () => extend_default, extendOwn: () => extendOwn_default, filter: () => filter$2, find: () => find$1, findIndex: () => findIndex_default, findKey: () => findKey$2, findLastIndex: () => findLastIndex_default, findWhere: () => findWhere, first: () => first$2, flatten: () => flatten$2, foldl: () => reduce_default, foldr: () => reduceRight_default, forEach: () => each, functions: () => functions$1, get: () => get, groupBy: () => groupBy_default, has: () => has$2, head: () => first$2, identity: () => identity, include: () => contains$1, includes: () => contains$1, indexBy: () => indexBy_default, indexOf: () => indexOf_default, initial: () => initial, inject: () => reduce_default, intersection: () => intersection, invert: () => invert, invoke: () => invoke_default, isArguments: () => isArguments_default, isArray: () => isArray_default, isArrayBuffer: () => isArrayBuffer_default, isBoolean: () => isBoolean$2, isDataView: () => isDataView_default, isDate: () => isDate_default, isElement: () => isElement, isEmpty: () => isEmpty$1, isEqual: () => isEqual, isError: () => isError_default, isFinite: () => isFinite$1, isFunction: () => isFunction_default, isMap: () => isMap_default, isMatch: () => isMatch, isNaN: () => isNaN$1, isNull: () => isNull, isNumber: () => isNumber_default, isObject: () => isObject$2, isRegExp: () => isRegExp_default, isSet: () => isSet_default, isString: () => isString_default, isSymbol: () => isSymbol_default, isTypedArray: () => isTypedArray_default, isUndefined: () => isUndefined$2, isWeakMap: () => isWeakMap_default, isWeakSet: () => isWeakSet_default, iteratee: () => iteratee, keys: () => keys$1, last: () => last$1, lastIndexOf: () => lastIndexOf_default, map: () => map$2, mapObject: () => mapObject, matcher: () => matcher, matches: () => matcher, max: () => max$2, memoize: () => memoize$2, methods: () => functions$1, min: () => min$2, mixin: () => mixin, negate: () => negate, noop: () => noop, now: () => now_default, object: () => object, omit: () => omit_default, once: () => once_default, pairs: () => pairs, partial: () => partial, partition: () => partition_default, pick: () => pick_default, pluck: () => pluck, property: () => property, propertyOf: () => propertyOf, random: () => random, range: () => range, reduce: () => reduce_default, reduceRight: () => reduceRight_default, reject: () => reject$1, rest: () => rest, restArguments: () => restArguments, result: () => result, sample: () => sample, select: () => filter$2, shuffle: () => shuffle, size: () => size, some: () => some$1, sortBy: () => sortBy, sortedIndex: () => sortedIndex, tail: () => rest, take: () => first$2, tap: () => tap, template: () => template, templateSettings: () => templateSettings_default, throttle: () => throttle, times: () => times, toArray: () => toArray, toPath: () => toPath$1, transpose: () => unzip, unescape: () => unescape_default, union: () => union_default, uniq: () => uniq$1, unique: () => uniq$1, uniqueId: () => uniqueId, unzip: () => unzip, values: () => values$2, where: () => where, without: () => without_default, wrap: () => wrap, zip: () => zip_default }), init_modules = __esmMin((() => {
|
|
32203
32235
|
init__setup(), init_restArguments(), init_isObject(), init_isNull(), init_isUndefined(), init_isBoolean(), init_isElement(), init_isString(), init_isNumber(), init_isDate(), init_isRegExp(), init_isError(), init_isSymbol(), init_isArrayBuffer(), init_isDataView(), init_isArray(), init_isFunction(), init_isArguments(), init_isFinite(), init_isNaN(), init_isTypedArray(), init_isEmpty(), init_isMatch(), init_isEqual(), init_isMap(), init_isWeakMap(), init_isSet(), init_isWeakSet(), init_keys(), init_allKeys(), init_values(), init_pairs(), init_invert(), init_functions(), init_extend(), init_extendOwn(), init_defaults(), init_create(), init_clone(), init_tap(), init_get(), init_has(), init_mapObject(), init_identity(), init_constant(), init_noop(), init_toPath(), init_property(), init_propertyOf(), init_matcher(), init_times(), init_random(), init_now(), init_escape(), init_unescape(), init_templateSettings(), init_template(), init_result(), init_uniqueId(), init_chain(), init_iteratee(), init_partial(), init_bind(), init_bindAll(), init_memoize(), init_delay(), init_defer(), init_throttle(), init_debounce(), init_wrap(), init_negate(), init_compose(), init_after(), init_before(), init_once(), init_findKey(), init_findIndex(), init_findLastIndex(), init_sortedIndex(), init_indexOf(), init_lastIndexOf(), init_find(), init_findWhere(), init_each(), init_map(), init_reduce(), init_reduceRight(), init_filter(), init_reject(), init_every(), init_some(), init_contains(), init_invoke(), init_pluck(), init_where(), init_max(), init_min(), init_shuffle(), init_sample(), init_sortBy(), init_groupBy(), init_indexBy(), init_countBy(), init_partition(), init_toArray(), init_size(), init_pick(), init_omit(), init_first(), init_initial(), init_last(), init_rest(), init_compact(), init_flatten(), init_without(), init_uniq(), init_union(), init_intersection(), init_difference(), init_unzip(), init_zip(), init_object(), init_range(), init_chunk(), init_mixin(), init_underscore_array_methods();
|
|
32204
32236
|
})), _$2, init_index_default = __esmMin((() => {
|
|
32205
32237
|
init_modules(), _$2 = mixin(modules_exports), _$2._ = _$2;
|
|
32206
|
-
})), index_all_exports = __exportAll({ VERSION: () => VERSION, after: () => after, all: () => every$1, allKeys: () => allKeys, any: () => some$1, assign: () => extendOwn_default, before: () => before, bind: () => bind_default, bindAll: () => bindAll_default, chain: () => chain, chunk: () => chunk, clone: () => clone, collect: () => map$2, compact: () => compact$1, compose: () => compose, constant: () => constant, contains: () => contains$1, countBy: () => countBy_default, create: () => create, debounce: () => debounce, default: () => _$2, defaults: () => defaults_default, defer: () => defer_default, delay: () => delay_default, detect: () => find$1, difference: () => difference_default, drop: () => rest, each: () => each, escape: () => escape_default, every: () => every$1, extend: () => extend_default, extendOwn: () => extendOwn_default, filter: () => filter$2, find: () => find$1, findIndex: () => findIndex_default, findKey: () => findKey, findLastIndex: () => findLastIndex_default, findWhere: () => findWhere, first: () => first$2, flatten: () => flatten$2, foldl: () => reduce_default, foldr: () => reduceRight_default, forEach: () => each, functions: () => functions$1, get: () => get, groupBy: () => groupBy_default, has: () => has$2, head: () => first$2, identity: () => identity, include: () => contains$1, includes: () => contains$1, indexBy: () => indexBy_default, indexOf: () => indexOf_default, initial: () => initial, inject: () => reduce_default, intersection: () => intersection, invert: () => invert, invoke: () => invoke_default, isArguments: () => isArguments_default, isArray: () => isArray_default, isArrayBuffer: () => isArrayBuffer_default, isBoolean: () => isBoolean$2, isDataView: () => isDataView_default, isDate: () => isDate_default, isElement: () => isElement, isEmpty: () => isEmpty$1, isEqual: () => isEqual, isError: () => isError_default, isFinite: () => isFinite$1, isFunction: () => isFunction_default, isMap: () => isMap_default, isMatch: () => isMatch, isNaN: () => isNaN$1, isNull: () => isNull, isNumber: () => isNumber_default, isObject: () => isObject$2, isRegExp: () => isRegExp_default, isSet: () => isSet_default, isString: () => isString_default, isSymbol: () => isSymbol_default, isTypedArray: () => isTypedArray_default, isUndefined: () => isUndefined$2, isWeakMap: () => isWeakMap_default, isWeakSet: () => isWeakSet_default, iteratee: () => iteratee, keys: () => keys$1, last: () => last$1, lastIndexOf: () => lastIndexOf_default, map: () => map$2, mapObject: () => mapObject, matcher: () => matcher, matches: () => matcher, max: () => max$2, memoize: () => memoize$2, methods: () => functions$1, min: () => min$2, mixin: () => mixin, negate: () => negate, noop: () => noop, now: () => now_default, object: () => object, omit: () => omit_default, once: () => once_default, pairs: () => pairs, partial: () => partial, partition: () => partition_default, pick: () => pick_default, pluck: () => pluck, property: () => property, propertyOf: () => propertyOf, random: () => random, range: () => range, reduce: () => reduce_default, reduceRight: () => reduceRight_default, reject: () => reject$1, rest: () => rest, restArguments: () => restArguments, result: () => result, sample: () => sample, select: () => filter$2, shuffle: () => shuffle, size: () => size, some: () => some$1, sortBy: () => sortBy, sortedIndex: () => sortedIndex, tail: () => rest, take: () => first$2, tap: () => tap, template: () => template, templateSettings: () => templateSettings_default, throttle: () => throttle, times: () => times, toArray: () => toArray, toPath: () => toPath$1, transpose: () => unzip, unescape: () => unescape_default, union: () => union_default, uniq: () => uniq$1, unique: () => uniq$1, uniqueId: () => uniqueId, unzip: () => unzip, values: () => values$2, where: () => where, without: () => without_default, wrap: () => wrap, zip: () => zip_default }), init_index_all = __esmMin((() => {
|
|
32238
|
+
})), index_all_exports = __exportAll({ VERSION: () => VERSION, after: () => after, all: () => every$1, allKeys: () => allKeys, any: () => some$1, assign: () => extendOwn_default, before: () => before, bind: () => bind_default, bindAll: () => bindAll_default, chain: () => chain, chunk: () => chunk, clone: () => clone, collect: () => map$2, compact: () => compact$1, compose: () => compose, constant: () => constant, contains: () => contains$1, countBy: () => countBy_default, create: () => create, debounce: () => debounce, default: () => _$2, defaults: () => defaults_default, defer: () => defer_default, delay: () => delay_default, detect: () => find$1, difference: () => difference_default, drop: () => rest, each: () => each, escape: () => escape_default, every: () => every$1, extend: () => extend_default, extendOwn: () => extendOwn_default, filter: () => filter$2, find: () => find$1, findIndex: () => findIndex_default, findKey: () => findKey$2, findLastIndex: () => findLastIndex_default, findWhere: () => findWhere, first: () => first$2, flatten: () => flatten$2, foldl: () => reduce_default, foldr: () => reduceRight_default, forEach: () => each, functions: () => functions$1, get: () => get, groupBy: () => groupBy_default, has: () => has$2, head: () => first$2, identity: () => identity, include: () => contains$1, includes: () => contains$1, indexBy: () => indexBy_default, indexOf: () => indexOf_default, initial: () => initial, inject: () => reduce_default, intersection: () => intersection, invert: () => invert, invoke: () => invoke_default, isArguments: () => isArguments_default, isArray: () => isArray_default, isArrayBuffer: () => isArrayBuffer_default, isBoolean: () => isBoolean$2, isDataView: () => isDataView_default, isDate: () => isDate_default, isElement: () => isElement, isEmpty: () => isEmpty$1, isEqual: () => isEqual, isError: () => isError_default, isFinite: () => isFinite$1, isFunction: () => isFunction_default, isMap: () => isMap_default, isMatch: () => isMatch, isNaN: () => isNaN$1, isNull: () => isNull, isNumber: () => isNumber_default, isObject: () => isObject$2, isRegExp: () => isRegExp_default, isSet: () => isSet_default, isString: () => isString_default, isSymbol: () => isSymbol_default, isTypedArray: () => isTypedArray_default, isUndefined: () => isUndefined$2, isWeakMap: () => isWeakMap_default, isWeakSet: () => isWeakSet_default, iteratee: () => iteratee, keys: () => keys$1, last: () => last$1, lastIndexOf: () => lastIndexOf_default, map: () => map$2, mapObject: () => mapObject, matcher: () => matcher, matches: () => matcher, max: () => max$2, memoize: () => memoize$2, methods: () => functions$1, min: () => min$2, mixin: () => mixin, negate: () => negate, noop: () => noop, now: () => now_default, object: () => object, omit: () => omit_default, once: () => once_default, pairs: () => pairs, partial: () => partial, partition: () => partition_default, pick: () => pick_default, pluck: () => pluck, property: () => property, propertyOf: () => propertyOf, random: () => random, range: () => range, reduce: () => reduce_default, reduceRight: () => reduceRight_default, reject: () => reject$1, rest: () => rest, restArguments: () => restArguments, result: () => result, sample: () => sample, select: () => filter$2, shuffle: () => shuffle, size: () => size, some: () => some$1, sortBy: () => sortBy, sortedIndex: () => sortedIndex, tail: () => rest, take: () => first$2, tap: () => tap, template: () => template, templateSettings: () => templateSettings_default, throttle: () => throttle, times: () => times, toArray: () => toArray, toPath: () => toPath$1, transpose: () => unzip, unescape: () => unescape_default, union: () => union_default, uniq: () => uniq$1, unique: () => uniq$1, uniqueId: () => uniqueId, unzip: () => unzip, values: () => values$2, where: () => where, without: () => without_default, wrap: () => wrap, zip: () => zip_default }), init_index_all = __esmMin((() => {
|
|
32207
32239
|
init_index_default(), init_modules();
|
|
32208
32240
|
}));
|
|
32209
32241
|
init_index_all();
|
|
@@ -38631,8 +38663,8 @@ class Indent {
|
|
|
38631
38663
|
}
|
|
38632
38664
|
}
|
|
38633
38665
|
const chrome$1 = "# Viewer chrome: buttons, panel headers, and other UI the reader interacts\n# with. Rendered on the main thread and selected by `uiLocale`.\n#\n# Message ids are lower-kebab-case Fluent identifiers, optionally with a\n# single `.attribute` suffix (`submit-button`, `answer-status.correct`).\n#\n# This catalog is the source of truth for every other locale: `lint:i18n`\n# rejects a translation that defines a key missing here. Run\n# `npm run codegen -w @doenet/i18n` after editing.\n\n\n## Answer submission — the check-work button and the status it reports.\n\nanswer-checking = Checking...\nanswer-submitting = Submitting...\n\n# Announced to a screen reader while the submission is in flight. Separate\n# from the button's own text, which is abbreviated.\nanswer-checking-status = Checking answer\nanswer-submitting-status = Submitting answer\n\nanswer-correct = Correct\nanswer-incorrect = Incorrect\n\n# Shown instead of a correctness verdict when the activity withholds\n# correctness: the response was recorded, nothing is claimed about it.\nanswer-response-saved = Response Saved\n\n# Partial credit. `-credit` is used when repeated attempts reduce the credit\n# available, `-correct` when they do not, and `-short` on a button too narrow\n# for a word.\nanswer-percent-credit = { $percent }% Credit\nanswer-percent-correct = { $percent }% Correct\nanswer-percent-short = { $percent } %\n\nmax-credit-available = Max credit available: { $percent }%\n\n# Fluent formats `{ $count }` with `Intl.NumberFormat`, so a four-digit\n# attempt count renders as \"1,000\" where the hand-built string said \"1000\".\n# That is the one place English output is not byte-identical to what this\n# replaced, and grouping is the locale-correct rendering, so it stands.\nattempts-remaining =\n { $count ->\n [0] no attempts remaining\n [one] { $count } attempt remaining\n *[other] { $count } attempts remaining\n }\n\n# Appended to an input's accessible name once its response has been graded,\n# so a screen reader reports the verdict along with the field.\nvalidation-correct = (Correct)\nvalidation-incorrect = (Incorrect)\nvalidation-partially-correct = (Partially correct)\n\n# Tooltip on the badge that reports how many responses have been submitted to\n# one answer, shown only to a host that asked for it. `$answerId` is the\n# answer's authored name and is never translated.\nanswer-show-responses =\n { $count ->\n [one] Show { $count } response to { $answerId }\n *[other] Show { $count } responses to { $answerId }\n }\n\n\n## Disclosure panels\n\nfeedback-heading = Feedback\n\n# Follows a disclosure panel's own heading — \"Solution (click to open)\" — and\n# is shared by `<solution>`, `<hint>`, and a collapsible `<section>`. The whole\n# parenthetical is one message: where the word for open or close falls inside\n# it is the translator's business.\ncollapsible-click-to-open = (click to open)\ncollapsible-click-to-close = (click to close)\n\n# Placeholder inside a panel that has been opened before its contents have\n# arrived from the core. Shared by `<solution>` and a collapsible `<section>`.\ncollapsible-initializing = Initializing...\n\n# Tooltip on a footnote marker, naming what activating it will do.\nfootnote-show = Show footnote\nfootnote-hide = Hide footnote\n\n# Tooltip on the ⓘ affordance that reveals an input's description.\ndescription-more-information = more information\n\n\n## Controls\n\nslider-previous = Prev\nslider-next = Next\n\nkeyboard-open = Open Keyboard\nkeyboard-close = Close Keyboard\n\n# Accessible name of the button that drops one selected choice from an inline\n# `<choiceInput selectMultiple>`. The button itself is drawn as a bare cross,\n# so `$choice` — the choice's own text, which is never translated — is the\n# only thing that says which chip it belongs to.\nchoice-input-remove-choice = Remove { $choice }\n\n# Accessible names of a matrix input's size controls, whose visible labels are\n# the symbols `r-` `r+` `c-` `c+`.\nmatrix-remove-row = Remove row\nmatrix-add-row = Add row\nmatrix-remove-column = Remove column\nmatrix-add-column = Add column\n\n# Modes and actions of the subset-of-reals input's control strip. The button\n# that selects all of the reals is the symbol `R`, not a word, and stays in\n# place.\nsubset-add-remove-points = Add/Remove points\nsubset-toggle-points-intervals = Toggle points and intervals\nsubset-move-points = Move Points\nsubset-clear = Clear\n\n# Buttons that edit an orbital diagram: rows hold boxes, boxes hold up to\n# three spin arrows.\norbital-add-row = Add Row\norbital-remove-row = Remove Row\norbital-add-box = Add Box\norbital-remove-box = Remove Box\norbital-add-up-arrow = Add Up Arrow\norbital-add-down-arrow = Add Down Arrow\norbital-remove-arrow = Remove Arrow\n\n# Accessible name of the text field naming one row of an orbital diagram,\n# counting from 1.\norbital-row-label = Label for row { $row }\n\n# Labels the answer column of a pretzel exercise's grid.\npretzel-answer = Answer\n\n# Caption above the table a `<summaryStatistics>` renders. It used to name the\n# data column being summarized; the statistics now come from values written in\n# the document, so there is no column to name and the message takes no\n# placeable. The table's own headings (`mean`, `stdev`, `quartile1`, …) are the\n# statistic ids an author references, not prose, and stay in place.\n#\n# Every translation of the old message named the column inside the sentence —\n# «Resumen estadístico de { $column }» — so none of them survives the placeable\n# being dropped, and the key was removed from the other catalogs rather than\n# truncated to whatever precedes the connector. Those locales fall back to\n# English here until the caption is translated again.\nsummary-statistics-caption = Summary statistics\n\n\n## Math input\n\n# Accessible name of the popover that previews the typed expression, and of\n# the rendered expression inside it.\nmath-input-preview-region = math expression preview\nmath-input-preview = Preview\nmath-input-invalid-expression = Invalid expression:\n\n\n## Document status\n\n# Shown while the core is still starting up and nothing can be rendered yet.\nviewer-initializing = Initializing...\n\n\n## Errors\n\n# Prefixes an error message wherever one is shown in place of content: an\n# `<error>` the core reported, the error boundary's fallback, and the\n# placeholder left where a renderer chunk failed to load.\nerror-heading = Error\n\n# Follows the error's own message inside that box, saying where in the source\n# the error was found. $span selects the sentence — one line or a range of\n# them — as a key rather than a phrase, so a translation is free to reorder the\n# whole of it. $startLine and $endLine are line numbers, and arrive as text\n# rather than as numbers so that line 1234 is not grouped as \"1,234\": a line\n# number is an identifier, not a quantity.\nerror-found-at =\n { $span ->\n [line] Found on line { $startLine }.\n *[lines] Found on lines { $startLine }–{ $endLine }.\n }\n\n# Banner above a document that compiled with at least one error in it.\ndocument-contains-errors = This document contains errors!\n\n# Headings of the tooltip the editor shows over a squiggle, naming what kind\n# of diagnostic it is. `-information` and `-hint` are styled identically but\n# are two different words; `-hint` is unreachable today, since nothing Doenet\n# raises is an LSP hint, and is here so the set is complete.\ndiagnostic-heading-error = Error\ndiagnostic-heading-warning = Warning\ndiagnostic-heading-information = Info\ndiagnostic-heading-hint = Hint\n\n# Headings of the same tooltip over an accessibility diagnostic, which says\n# what kind of problem it is rather than how severe. Level 1 is a failure\n# against the WCAG AA success criteria; level 2 is Doenet's own advice, which\n# no standard requires.\naccessibility-heading-level-1 = WCAG AA Accessibility Violation\naccessibility-heading-level-2 = Accessibility alert\n\n# Shown in place of the document when a renderer threw and the error boundary\n# caught it.\nsomething-went-wrong = Something went wrong.\n\n# Shown in place of a single renderer whose code chunk never arrived.\nrenderer-load-failed = a renderer failed to load. Please reload the page.\n\n# Shown in place of the document when the core worker could not be started\n# after retries, rather than leaving the pane blank.\ncore-start-failed = This document could not be started. Please reload the page.\n\n# Replaces `core-start-failed` when several documents were starting at the\n# same time — counted across every same-origin tab, so not necessarily all on\n# the page the reader sees. Naming the contention keeps the reader from\n# concluding the service is broken.\ncore-start-failed-busy = This document could not be started. Several documents were starting at once, which can take longer on a slower device. Reloading the page may help once the other documents have finished.\n\n# Shown for a core-start failure the reader can still retry, next to the\n# button that does it (`core-start-retry`). It leaves out the advice to\n# reload that the messages above carry: the button is the cheaper action,\n# and a reader told to reload will reload the whole page — restarting every\n# other document on it, which is what produced the failure in the first\n# place on a page that was already busy.\ncore-start-failed-retry = This document could not be started.\n\n# The contended variant of `core-start-failed-retry`, named for the same\n# reason `core-start-failed-busy` is: a reader who is told that several\n# documents were starting at once has a reason to try again in a moment\n# rather than concluding the service is broken.\ncore-start-failed-busy-retry = This document could not be started. Several documents were starting at once, which can take longer on a slower device.\n\n# Label of the button that starts a failed document over without reloading\n# the page. Offered once per document; a retry that fails too falls back to\n# `core-start-failed` / `core-start-failed-busy`, which advise the reload.\ncore-start-retry = Try again\n\n# Shown beside a document that is on screen and working, when the host\n# answered `SPLICE.getState` with an error and so the document started\n# without the reader's saved work (#1741). The host's own wording follows\n# this lead-in, so it says what was lost rather than why.\nsaved-state-unavailable = Your saved work could not be loaded.\n";
|
|
38634
|
-
const content = '# Worker-generated content: style descriptions ("thick red line"), boolean\n# words, and other prose the core computes into the document. Selected by\n# `documentLocale`, which follows the content\'s language rather than the\n# reader\'s UI language.\n#\n# Message ids are lower-kebab-case Fluent identifiers, optionally with a\n# single `.attribute` suffix (`color.blue`, `noun.line-segment`).\n\n\n## Style vocabulary\n##\n## The words the style pipeline derives from a component\'s numeric and\n## enumerated style values. A word an author writes directly — `lineColorWord`,\n## `markerStyleWord`, and their siblings — passes through untranslated: the\n## author chose those words, and rewriting them would be a surprise. So does a\n## CSS named color asked for by name ("rebeccapurple"), which\n## `resolveColorWord` deliberately preserves.\n##\n## Every adjective here is handed `$gender`, the grammatical gender of the noun\n## it describes (see `noun-gender`), and `$role`, the syntactic position the\n## phrase it belongs to is going into. English has no agreement and ignores\n## both; a language that inflects selects on them.\n##\n## `$role` is one of:\n##\n## standalone the phrase stands on its own, which is where all but\n## the three below are\n## border-clause inside `style-border-clause`, behind its preposition\n## background-clause the background colour inside `style-text`, behind its\n## preposition\n## text-clause the text colour beside it, inside `style-text`\n##\n## The last three exist because those words are rendered in two places each —\n## once alone, as `borderStyleDescription`, `backgroundColor` and `textColor`\n## report them, and once embedded in a clause. A language that inflects for\n## case needs a different form in each, and no amount of `$gender` can say\n## which is wanted. Which case a position governs is the catalog\'s business:\n## `border-clause` is a dative in German, an instrumental in Russian and\n## Polish, and nothing at all in a language that does not inflect.\n\n# The canonical color families a color value resolves to.\ncolor =\n .black = black\n .white = white\n .gray = gray\n .red = red\n .orange = orange\n .yellow = yellow\n .green = green\n .cyan = cyan\n .blue = blue\n .purple = purple\n .pink = pink\n .brown = brown\n\n# Stroke widths. Only the extremes are named — a middling width is described by\n# its color alone.\nline-width =\n .thick = thick\n .thin = thin\n\n# Dash patterns. A solid stroke is described by its color alone.\nline-style =\n .dashed = dashed\n .dotted = dotted\n\n# Patterns a shape\'s interior can be filled with. A solid fill is described by\n# its color alone.\nfill-style =\n .horizontal = horizontal lines\n .vertical = vertical lines\n .diagonal = diagonal lines\n .backdiagonal = reverse diagonal lines\n .dots = dots\n .diamonds = diamonds\n\n# The things being described. The shapes a point can be drawn as ("square",\n# "cross") are nouns too: a point\'s description names its marker shape rather\n# than always saying "point".\nnoun =\n .line = line\n .line-segment = line segment\n .ray = ray\n .vector = vector\n .curve = curve\n .function = function\n .slope-field = slope field\n .vector-field = vector field\n .parabola = parabola\n .polyline = polyline\n .polygon = polygon\n .triangle = triangle\n .rectangle = rectangle\n .circle = circle\n .region = region\n .point = point\n .square = square\n .diamond = diamond\n .cross = cross\n .plus = plus\n\n# A regular polygon names its side count, so it is a message of its own rather\n# than one of `noun`\'s attributes.\n#\n# `$part` splits the noun where a language needs it split: `head` is the word\n# the adjectives attach to, `tail` a complement that follows them. English\n# folds the side count into the head and has no tail; Spanish says "polígono\n# regular" and puts "de 5 lados" after the adjectives, so that they stay beside\n# the noun they agree with. `style-with-noun` and `style-filled-with-noun`\n# place the two halves.\n#\n# `$numSides` is a real number, so it is formatted by the locale\'s own rules —\n# a 1000-gon reads "1,000-sided" here and "de 1000 lados" in Spanish. That is\n# the number-formatting policy in the README, and the one place a description\n# is not character-for-character what the pre-catalog code produced.\nnoun-regular-polygon =\n { $part ->\n [tail] { "" }\n *[head] { $numSides }-sided regular polygon\n }\n\n# The grammatical gender of the noun being described, passed to every adjective\n# describing it so that translations can agree. English has no grammatical\n# gender, so every noun answers the same and the answer goes unused.\n#\n# `$noun` is one of `noun`\'s attribute names, `regular-polygon` for the shape\n# `noun-regular-polygon` names, or the head of a phrase the description builds\n# without naming it as a noun: `border`, `fill`, `text`, or `background`. A\n# word this message does not list falls to its default gender — which is also\n# what an author\'s own `markerStyleWord` gets, since the catalog has never seen\n# it.\nnoun-gender = neuter\n\n\n## Style composition\n##\n## `$parts` names which pieces the style actually supplies, so that a\n## translation can order and inflect each combination on its own terms instead\n## of substituting into a fixed English frame. An absent piece is a different\n## branch, never an empty placeable.\n\n# The adjectives describing a stroke: its width, its dash pattern, and its\n# color. Also describes a shape\'s border, where the color is dropped when it\n# matches the fill it surrounds.\nstyle-stroke =\n { $parts ->\n [width-style-color] { $width } { $lineStyle } { $color }\n [width-color] { $width } { $color }\n [style-color] { $lineStyle } { $color }\n [width-style] { $width } { $lineStyle }\n [width] { $width }\n [style] { $lineStyle }\n *[color] { $color }\n }\n\n# A style description followed by what it describes: "thick red line".\n#\n# `$nounTail` is the noun\'s trailing complement, for the nouns whose\n# translation splits around the adjectives (see `noun-regular-polygon`).\n# English has none today, so it only ever selects `noun` for itself — the other\n# variant is still what a partly-translated locale falls back to, and dropping\n# it would drop that locale\'s side count.\n#\n# The phrase it builds is never governed by anything, so its `$role` is always\n# `standalone`.\nstyle-with-noun =\n { $parts ->\n [noun-tail] { $description } { $noun } { $nounTail }\n *[noun] { $description } { $noun }\n }\n\n# The word marking a shape as filled.\n#\n# A key of its own, looked up by the code and handed to the messages below as\n# `$filled`, rather than literal text inside them: a language that inflects it\n# has to agree it with the shape. Referencing it from those messages would not\n# do — a term reference (`{ -filled }`) gets an empty scope and never sees\n# `$gender`, and a message reference resolves only inside its own bundle, so a\n# locale that translated `style-filled` but not this word would render the\n# reference literally instead of falling back to English.\n#\n# Said only of the shape itself, so its `$role` is always `standalone`.\nstyle-filled-word = filled\n\n# A filled shape, and the pattern its interior is drawn with, if any.\nstyle-filled =\n { $parts ->\n [pattern] { $filled } { $color } with { $pattern }\n *[plain] { $filled } { $color }\n }\n\n# The same, naming the shape: "filled blue circle with diamonds".\n#\n# The `-tail` variants carry the noun\'s trailing complement, as\n# `style-with-noun` does.\nstyle-filled-with-noun =\n { $parts ->\n [pattern] { $filled } { $color } { $noun } with { $pattern }\n [plain-tail] { $filled } { $color } { $noun } { $nounTail }\n [pattern-tail] { $filled } { $color } { $noun } { $nounTail } with { $pattern }\n *[plain] { $filled } { $color } { $noun }\n }\n\n# The border clause appended to a filled shape: "with a thick red border".\n#\n# `$parts` carries two distinctions English cares about: whether a fill pattern\n# was already mentioned, which makes this a further clause ("and") rather than\n# the first ("with"), and whether the surrounding description named the shape,\n# which is where English wants an article.\nstyle-border-clause =\n { $parts ->\n [with-article] with a { $border } border\n [and] and { $border } border\n [and-article] and a { $border } border\n *[with] with { $border } border\n }\n\n# How a shape\'s interior is filled, on its own: "blue diamonds".\nstyle-fill =\n { $parts ->\n [pattern] { $color } { $pattern }\n *[plain] { $color }\n }\n\nstyle-unfilled = unfilled\n\n# How a piece of text is styled: its color, and the background behind it.\n#\n# The only composition message with no `$role`: its two words sit in two\n# different positions, so they arrive already inflected for `text-clause` and\n# `background-clause` respectively and no one token would describe them both.\nstyle-text =\n { $parts ->\n [background] { $color } with a { $background } background\n *[plain] { $color }\n }\n\n# What `backgroundColor` answers when nothing is drawn behind the text.\nstyle-background-none = none\n\n\n## Boolean words\n##\n## What a `<boolean>` or `<booleanInput>` *displays*.\n##\n## Not what it parses or serializes. `true` and `false` are DoenetML syntax —\n## an author writes them in the source, `<award>` compares against them, and\n## saved state stores them — so they stay English everywhere, in every\n## language, exactly as `<` stays `<`. Only the word the reader sees moves.\n##\n## The consequence is that a value has two spellings in a translated document,\n## and both have to be accepted where one is read back in: see\n## `booleanFromWord` in the worker.\n\nboolean-true = true\nboolean-false = false\n\n\n## Answer buttons\n##\n## The default text on an answer\'s submit button. Only the *default* is\n## translated: an author who writes `submitLabel="Ready?"` gets "Ready?" in\n## every locale, because they chose those words for their document and a\n## translation of it is not Doenet\'s to make.\n\n# Shown when the answer will report whether the response was right.\nanswer-submit-label = Check Work\n\n# Shown when it will not — the reader submits, and is told nothing more.\nanswer-submit-label-no-correctness = Submit Response\n\n\n## Sectional blocks\n##\n## The word a sectional block calls itself, which the reader sees in its\n## heading and on a `<solution>`\'s reveal control. Keyed by the element the\n## author writes, so `<subsection>` and `<subsubsection>` share `.section` —\n## they are all called a section.\n##\n## An author who writes `renameTo` has named the block themselves, and that\n## name passes through in every language.\n\nsection-name =\n .activity = Activity\n .aside = Aside\n .cascade = Cascade\n .definition = Definition\n .example = Example\n .exercise = Exercise\n .exercises = Exercises\n .given-answer = Answer\n .note = Note\n .objectives = Objectives\n .paragraphs = Paragraphs\n .part = Part\n .problem = Problem\n .problems = Problems\n .proof = Proof\n .question = Question\n .section = Section\n .solution = Solution\n .task = Task\n .theorem = Theorem\n\n# The heading a section builds for itself: "Example 2", "Section 1.3: Limits".\n#\n# `$parts` names which pieces are present and whether a `<title>` follows, so\n# that a translation orders and punctuates each combination on its own terms\n# rather than substituting into an English frame. English puts the word first\n# and separates a title with a colon — or with a period when the heading is a\n# bare number — and none of that is a given.\n#\n# The `-title` variants end in the separator, because what follows them is the\n# title child, rendered separately. Fluent trims trailing whitespace, so the\n# separator is written as a string literal to keep its space.\n#\n# `$sectionNumber` arrives as text ("2", "1.3") rather than as a number: it is\n# an identifier made of counters, and it is not the catalog\'s to reformat.\nsection-title-prefix =\n { $parts ->\n [name] { $sectionName }\n [number] { $sectionNumber }\n [name-title] { $sectionName }{ ": " }\n [number-title] { $sectionNumber }{ ". " }\n [name-number-title] { $sectionName } { $sectionNumber }{ ": " }\n *[name-number] { $sectionName } { $sectionNumber }\n }\n\n# The heading a `<hint>` shows when the author gave it no `<title>`.\nhint-title = Hint\n\n\n## Tables and figures\n##\n## The name a `<table>` or `<figure>` gives itself, which the renderer sets in\n## bold at the head of the title or caption: "Table 2", "Figure 3". An\n## unnumbered one is named by the bare word.\n##\n## The word and the number are one message rather than a word the code\n## concatenates a number onto, so that a language which orders or punctuates\n## them differently can say so.\n##\n## `$enumeration` arrives as text rather than as a number: it identifies the\n## table, so the thousandth one is "Table 1000" and not "Table 1,000".\n##\n## The `-title` and `-caption` branches end in the separator joining the name\n## to the authored title or caption, which the renderer used to write as a\n## literal `": "` of its own. That child is arbitrary marked-up content\n## rendered after this string rather than an argument passed into it, so where\n## it sits is the one thing a translation cannot reorder — the same shape\n## `section-title-prefix` has.\n\n## The name a `<chart>` gives a series that the author did not label.\n##\n## This is not decoration: it is the text a screen reader announces on the\n## level between the chart and its marks, so a reader stopping there is told\n## which group they have reached. A bare number would be indistinguishable\n## from a value or a category, which are what the levels either side of it\n## announce.\n##\n## `$position` arrives as text rather than as a number, for the reason\n## `table-name`\'s `$enumeration` does: it identifies the series, so the\n## thousandth one is "series 1000" and not "series 1,000".\nchart-unlabeled-series =\n series { $position }\n\ntable-name =\n { $parts ->\n [numbered] Table { $enumeration }\n [numbered-title] Table { $enumeration }{ ": " }\n [unnumbered-title] Table{ ": " }\n *[unnumbered] Table\n }\n\nfigure-name =\n { $parts ->\n [numbered] Figure { $enumeration }\n [numbered-caption] Figure { $enumeration }{ ": " }\n [unnumbered-caption] Figure{ ": " }\n *[unnumbered] Figure\n }\n\n\n## Paginator controls\n##\n## The strip of controls that steps through a `<paginator>`\'s pages. All three\n## words are author-overridable attributes, so only the default is translated.\n\npaginator-previous = Previous\npaginator-next = Next\npaginator-page = Page\n\n# The status between the two buttons: "Page 3 of 5".\n#\n# Composed in the worker rather than in the renderer so that the whole of it is\n# in one language: `$pageLabel` is the `pageLabel` attribute, which an author\n# may have written themselves, and the words joining it to the counts have to\n# sit beside it in the same tongue.\n#\n# The two counts arrive as text, for the same reason `$enumeration` does above.\npaginator-page-status = { $pageLabel } { $currentPage } of { $numPages }\n\n\n## Piecewise functions\n##\n## `<piecewiseFunction>` renders each branch\'s domain as mathematics and writes\n## these three words around it: "if 1 < x < 2 or 4 < x < 5", and "otherwise"\n## for the branch that catches what the others leave. The inequalities are\n## notation and stay as they are; these are prose, and are read aloud as prose.\n##\n## `or` is the only conjunction the core composes into content. Everything else\n## that reads as "a, b, and c" is a diagnostic, and those are joined by\n## `Intl.ListFormat` at the moment the reader\'s language is known rather than\n## by a catalog word (see `DiagnosticListArg`).\n\npiecewise-condition-or = or\n\n# Introduces the domain a branch applies on; the mathematics follows it.\npiecewise-condition-if = if\n\n# The last branch, which applies wherever none of the earlier ones do.\npiecewise-condition-otherwise = otherwise\n\n\n## Chemistry\n##\n## The names the chemistry components generate. Symbols (`H`, `Fe`), formulas,\n## and anything an author\'s `<award>` compares against by value stay as they\n## are; only what is displayed as prose is here.\n##\n## Element names are keyed by symbol, which is the identifier the atom database\n## is read through and is itself never translated.\n##\n## An element\'s periodic group, its phase at STP and its metal category are not\n## here. They are enumerated values an author\'s `<award>` compares against —\n## `$atom.groupName = Noble Gas` — and translating one would break that\n## comparison the moment a document declared a language (#1577).\n\nelement-name =\n .h = Hydrogen\n .he = Helium\n .li = Lithium\n .be = Beryllium\n .b = Boron\n .c = Carbon\n .n = Nitrogen\n .o = Oxygen\n .f = Fluorine\n .ne = Neon\n .na = Sodium\n .mg = Magnesium\n .al = Aluminum\n .si = Silicon\n .p = Phosphorus\n .s = Sulfur\n .cl = Chlorine\n .ar = Argon\n .k = Potassium\n .ca = Calcium\n .sc = Scandium\n .ti = Titanium\n .v = Vanadium\n .cr = Chromium\n .mn = Manganese\n .fe = Iron\n .co = Cobalt\n .ni = Nickel\n .cu = Copper\n .zn = Zinc\n .ga = Gallium\n .ge = Germanium\n .as = Arsenic\n .se = Selenium\n .br = Bromine\n .kr = Krypton\n .rb = Rubidium\n .sr = Strontium\n .y = Yttrium\n .zr = Zirconium\n .nb = Niobium\n .mo = Molybdenum\n .tc = Technetium\n .ru = Ruthenium\n .rh = Rhodium\n .pd = Palladium\n .ag = Silver\n .cd = Cadmium\n .in = Indium\n .sn = Tin\n .sb = Antimony\n .te = Tellurium\n .i = Iodine\n .xe = Xenon\n .cs = Cesium\n .ba = Barium\n .la = Lanthanum\n .ce = Cerium\n .pr = Praseodymium\n .nd = Neodymium\n .pm = Promethium\n .sm = Samarium\n .eu = Europium\n .gd = Gadolinium\n .tb = Terbium\n .dy = Dysprosium\n .ho = Holmium\n .er = Erbium\n .tm = Thulium\n .yb = Ytterbium\n .lu = Lutetium\n .hf = Hafnium\n .ta = Tantalum\n .w = Tungsten\n .re = Rhenium\n .os = Osmium\n .ir = Iridium\n .pt = Platinum\n .au = Gold\n .hg = Mercury\n .tl = Thallium\n .pb = Lead\n .bi = Bismuth\n .po = Polonium\n .at = Astatine\n .rn = Radon\n .fr = Francium\n .ra = Radium\n .ac = Actinium\n .th = Thorium\n .pa = Protactinium\n .u = Uranium\n .np = Neptunium\n .pu = Plutonium\n .am = Americium\n .cm = Curium\n .bk = Berkelium\n .cf = Californium\n .es = Einsteinium\n .fm = Fermium\n .md = Mendelevium\n .no = Nobelium\n .lr = Lawrencium\n .rf = Rutherfordium\n .db = Dubnium\n .sg = Seaborgium\n .bh = Bohrium\n .hs = Hassium\n .mt = Meitnerium\n .ds = Darmstadtium\n .rg = Roentgenium\n .cn = Copernicium\n .nh = Nihonium\n .fl = Flerovium\n .mc = Moscovium\n .lv = Livermorium\n .ts = Tennessine\n .og = Oganesson\n\n# The name an anion takes, for the elements that have one.\n#\n# English derives these — strip a trailing "ine", add "ide", with a small table\n# for the words that rule does not fit — but that is English morphology, not a\n# fact about anions. So each language supplies the names it uses. An element\n# with no entry here is called by its own name, which is also what the English\n# rule does for everything it does not match.\nelement-anion-name =\n .h = Hydride\n .c = Carbide\n .n = Nitride\n .o = Oxide\n .f = Fluoride\n .p = Phosphide\n .s = Sulfide\n .cl = Chloride\n .br = Bromide\n .i = Iodide\n .at = Astatide\n .ts = Tennesside\n\n# A transition metal\'s ion, named with its oxidation state: "Iron (II)".\n#\n# The Roman numeral is IUPAC notation and is the same in every language. Where\n# it sits and how it is punctuated is not, which is why this is a message and\n# not a suffix the code appends.\nion-name-oxidation-state = { $name } ({ $numeral })\n\n# Shown in place of a chemical symbol or formula that names nothing. Rendered\n# into mathematics as well as into text, so the brackets around it are added by\n# the code — inside `\\text{}` for the LaTeX form — and are not part of the\n# message.\nchemistry-invalid-symbol = Invalid Chemical Symbol\nchemistry-invalid-ionic-compound = Invalid Ionic Compound\n\n## Inputs embedded in math\n\n# Names the gap an input fills when it is drawn inside a typeset expression, so\n# that a screen reader reads "x equals blank plus 3" rather than skipping over\n# it. Written into the mathematics itself and never shown on screen, so keep it\n# to a word or two.\nmath-embedded-input-blank = blank\n\n# The same, when one expression has more than one gap, so that a reader can tell\n# which one they have reached.\nmath-embedded-input-blank-ordinal = blank { $ordinal } of { $total }\n\n# The five-number summary a `<chart type="box">` gives a screen reader on each\n# box it draws.\n#\n# Words rather than five bare numbers, which is the one annotation in a chart\n# that cannot do without them: a bar is announced as its category and its\n# value, and a point as its coordinates, because in both the position says\n# which number it is. A box reports five numbers at one position, and nothing\n# but the naming tells them apart.\n#\n# Each number arrives as text, already written the way the chart writes the\n# numbers on its own axis. Passed as a number instead, a translation would\n# format it a second time — grouping it, and rounding it to three fraction\n# digits — so a reader would hear a different figure from the one drawn.\nchart-box-summary =\n minimum { $minimum }, first quartile { $quartile1 }, median { $median }, third quartile { $quartile3 }, maximum { $maximum }\n\n# The name a `<chart type="box">` gives one observation drawn beyond a whisker.\n#\n# Named rather than announced as a bare number, for the reason the summary\n# above is worded: after the five numbers of the box, a further number on its\n# own says nothing about why it is drawn apart from them.\nchart-box-outlier =\n outlier { $value }\n';
|
|
38635
|
-
const diagnostics = '# Errors and warnings surfaced to the reader or author. Produced by the worker\n# but addressed to whoever is looking at the screen, so these are selected by\n# `uiLocale`, not `documentLocale`.\n#\n# Message ids are lower-kebab-case Fluent identifiers, optionally with a\n# single `.attribute` suffix (`invalid-attribute-value`).\n#\n# Reached by stable diagnostic code rather than by a literal `t("key")` call:\n# `DIAGNOSTIC_CODES` in `src/diagnostics.ts` maps `doenet-w0001` to the id\n# below, and `lint:i18n` treats that registry as the call site. Adding a\n# message here without registering a code for it fails the lint as an orphan.\n#\n# Translators: `through`, `endpoint`, `midpointOffset`, `numDimensions` and the\n# like are DoenetML attribute names. They are part of the language, not prose,\n# and must be left in English exactly as written.\n\n## `<lineSegment>`\n\n# $attributes is a list of attribute names; $attributesCount is its length.\nline-segment-attributes-ignored-with-endpoints =\n { $attributesCount ->\n [one] { $attributes } is ignored when two endpoints are specified\n *[other] { $attributes } are ignored when two endpoints are specified\n }\n\n# $attributes is a list of attribute names; $attributesCount is its length.\nline-segment-attributes-ignored-with-endpoint-and-midpoint =\n { $attributesCount ->\n [one] { $attributes } is ignored when an endpoint and a midpoint are both specified\n *[other] { $attributes } are ignored when an endpoint and a midpoint are both specified\n }\n\nline-segment-midpoint-offset-without-midpoint = midpointOffset has no effect without a midpoint\n\n## `<line>`\n\nline-points-undetermined-dimensions = Line through points of undetermined dimensions.\n\nline-points-too-few-dimensions = Line must be through points of at least two dimensions.\n\n# $variables is a bare enumeration of variable names, not an "and" list.\nline-points-depend-on-variables = Line is through points that depend on variables: { $variables }.\n\nline-equation-invalid-format = Invalid format for equation of line in variables { $variable1 } and { $variable2 }.\n\n## `<ray>`\n\nray-overprescribed-through = Ray is prescribed by through, endpoint, and direction. Ignoring specified through.\n\nray-dimension-mismatch = numDimensions mismatch in ray.\n\n## `<vector>`\n\nvector-overprescribed-head = Vector is prescribed by head, tail, and displacement. Ignoring specified head.\n\nvector-dimension-mismatch = numDimensions mismatch in vector.\n\n## Attracting and constraining\n\n# $component is the DoenetML tag of the child that was named, e.g. "polygon".\nattract-to-without-nearest-point = Cannot attract to a `<{ $component }>` as it doesn\'t have a nearestPoint state variable.\n\nconstrain-to-without-nearest-point = Cannot constrain to a `<{ $component }>` as it doesn\'t have a nearestPoint state variable.\n\nconstrain-to-interior-without-nearest-point = Cannot constrain to interior of a `<{ $component }>` as it doesn\'t have a nearestPoint state variable.\n\n## `<choiceInput>`\n\n# Translators: `labelPosition` is an attribute name and stays in English.\nchoice-input-label-position-ignored = labelPosition is ignored for non-inline choiceInput\n\n## Ordering children by index\n##\n## These name the component in prose rather than as a tag, matching how the\n## messages have always read. The component names stay in English; the nouns\n## around them are prose and should be translated.\n\nchoice-input-indices-count-mismatch = Ignoring indices specified for choiceInput as number of indices doesn\'t match number of choice children.\n\npretzel-indices-count-mismatch = Ignoring indices specified for problem as number of indices doesn\'t match number of problem children.\n\nshuffle-indices-count-mismatch = Ignoring indices specified for shuffle as number of indices doesn\'t match number of components.\n\n# $component is `choiceInput`, `pretzel` or `shuffle` — a DoenetML component\n# name, so it stays in English.\nindices-ignored-out-of-range = Ignoring indices specified for { $component } as some indices out of range.\n\npretzel-indices-repeated = Ignoring indices specified for pretzel as some indices are repeated.\n\npretzel-circuit-first-index = Ignoring indices specified for pretzel in circuit mode as the first index must be 1.\n\n## `<shuffle>` and `<sort>`\n\n# $component is `shuffle` or `sort`. These two components accept the same\n# children and fail the same ways, so they share their messages.\nstring-children-need-type = For `<{ $component }>` to work with string children, a `type` attribute must be specified.\n\n# $type is what the author wrote; math, text, number and boolean are attribute\n# values and stay in English.\ninvalid-type-defaulting-to-math = Invalid type { $type } for { $component } component. Must be one of math, text, number, or boolean. Defaulting to math.\n\n# Raised when `type` names something that is not one of the four readings. The\n# value is dropped rather than replaced by a guess, so the children are read as\n# though no type had been written. $type is what the author wrote.\ninvalid-type-ignored =\n Invalid type { $type } for { $component } component. Must be one of math, text, number, or boolean. Ignoring it and reading the values as though no type had been given.\n\n# $value is the string child that could not be used.\nstring-not-valid-component-to-arrange = String "{ $value }" is not a valid component to { $component }. Ignoring.\n\n## Types and variables\n\ninvalid-type-defaulting-to-number = Invalid type { $type }, setting type to number.\n\ninvalid-variable-value = Invalid value of a variable: `{ $value }`\n\n## Variants\n\n# $index is what the author wrote, reproduced verbatim rather than as a number:\n# it reached this message precisely because it was not one.\nvariant-index-must-be-number = Variant index { $index } must be a number\n\nvariant-index-must-be-integer = Variant index { $index } must be an integer\n\n## `<sideBySide>`\n\n# $component is `sideBySide` or `sbsGroup`.\nside-by-side-absolute-widths = `<{ $component }>` is not implemented for absolute measurements. Setting widths to relative.\n\nside-by-side-absolute-margins = `<{ $component }>` is not implemented for absolute measurements. Setting margins to relative.\n\nside-by-side-no-block-child = Invalid `<{ $component }>`: it must have at least one block child.\n\n## `<label>`\n\n# Translators: `for` is an attribute name and stays in English.\nlabel-for-ignored-on-graphical = The `for` attribute on graphical `<label>` is ignored.\n\nlabel-for-must-resolve-to-one = The `for` attribute on `<label>` must resolve to exactly one component.\n\nlabel-for-unresolved = The `for` attribute on `<label>` could not be resolved to a component.\n\nlabel-for-answer-with-authored-inputs = The `for` attribute on `<label>` references an `<answer>` with explicitly authored inputs; reference the input directly.\n\nlabel-for-answer-without-input = The `for` attribute on `<label>` references an `<answer>` without an input to label.\n\nlabel-for-must-reference-input-or-answer = The `for` attribute on `<label>` must reference an input or an answer.\n\n## Accessibility\n\n# $component is a DoenetML tag, e.g. "graph" or "image".\naccessibility-short-description-or-decorative = For accessibility, `<{ $component }>` must either have a short description or be specified as decorative.\n\naccessibility-video-short-description = For accessibility, `<video>` must have a short description.\n\naccessibility-input-short-description-or-label = For accessibility, `<{ $component }>` must have a short description or a label.\n\n# The companion to the message above, for the input an `<answer>` creates on the\n# author\'s behalf. Two messages rather than one with the subject passed in: the\n# subject is a phrase here, not a name, and a phrase handed over as an argument\n# would never reach a translator.\naccessibility-answer-input-short-description-or-label = For accessibility, an `<answer>` creating an input must have a short description or a label.\n\naccessibility-short-description-contains-math = Short descriptions should not contain math components such as `<{ $component }>`. Spell out any math with words.\n\n# $colorName is an attribute name and stays in English. $ratio and $threshold\n# are contrast ratios; $mode says which theme the shortfall was measured in,\n# and is `dark` or `light`.\naccessibility-section-title-insufficient-contrast =\n { $mode ->\n [dark] { $colorName } has insufficient contrast for the section heading text (dark mode) ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1).\n *[other] { $colorName } has insufficient contrast for the section heading text ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1).\n }\n\n## `<circle>`\n\n# $count is the number of through points.\ncircle-through-points-non-numerical = Haven\'t implemented `<circle>` through { $count } points in case where the points don\'t have numerical values.\n\ncircle-too-many-through-points = Cannot calculate circle through more than 3 points.\n\ncircle-overprescribed-radius-center-points = Cannot calculate circle with specified radius, center and through points.\n\ncircle-center-with-multiple-points = Cannot calculate circle with specified center through more than 1 point.\n\n# $distance and $radius arrive as strings, not numbers: $radius is the author\'s\n# own value echoed back for diagnosis, and formatting it as a quantity would\n# round a radius of 0.0001 away to 0.\ncircle-radius-too-small = Cannot calculate circle: given that the distance between the two points is { $distance }, the specified radius { $radius } is too small.\n\ncircle-radius-with-many-points = Cannot create circle through more than two points with a specified radius.\n\ncircle-invalid-center-or-through-points = Invalid center or through points of circle.\n\ncircle-radius-center-with-multiple-points = Cannot calculate radius of circle with specified center through more than 1 point.\n\ncircle-change-radius-non-numerical = Cannot change radius of circle with non-numerical through points\n\ncircle-radius-with-points-non-numerical = Cannot create circle through more than one point with specified radius when don\'t have numerical values.\n\ncircle-change-center-non-numerical = Haven\'t implemented changing center of circle through points with non numerical values.\n\n## `<function>`\n\n# Two independent counts in one sentence, so the variants multiply out. A\n# select\'s variants each need their own line, so the inner one spans lines too;\n# that is safe because newlines inside a placeable never reach the rendered\n# value. Only text continuing onto a further line would.\nfunction-domain-insufficient-dimensions =\n { $intervals ->\n [one] Insufficient dimensions for domain for function. Domain has { $intervals } interval but the function has { $inputs ->\n [one] { $inputs } input\n *[other] { $inputs } inputs\n }.\n *[other] Insufficient dimensions for domain for function. Domain has { $intervals } intervals but the function has { $inputs ->\n [one] { $inputs } input\n *[other] { $inputs } inputs\n }.\n }\n\nfunction-domain-invalid-format = Invalid format for domain for function.\n\n# $type is what was being read off the point. It selects the wording rather\n# than being substituted into it: "maximum", "slope" and the rest are English\n# nouns, and a noun handed over as an argument would never reach a translator.\n# The catch-all reproduces the pre-catalog behavior for a value not listed here.\nfunction-ignoring-non-numerical =\n { $type ->\n [maximum] Ignoring non-numerical maximum of function.\n [minimum] Ignoring non-numerical minimum of function.\n [extremum] Ignoring non-numerical extremum of function.\n [point] Ignoring non-numerical point of function.\n [slope] Ignoring non-numerical slope of function.\n *[other] Ignoring non-numerical { $type } of function.\n }\n\nfunction-ignoring-empty =\n { $type ->\n [maximum] Ignoring empty maximum of function.\n [minimum] Ignoring empty minimum of function.\n [extremum] Ignoring empty extremum of function.\n [point] Ignoring empty point of function.\n *[other] Ignoring empty { $type } of function.\n }\n\nfunction-points-too-close = Function contains two points with locations too close together. Can\'t define function.\n\nfunction-iterates-input-output-mismatch =\n { $inputs ->\n [one] Function iterates are possible only if the number of inputs of the function is equal to the number of outputs. This function has { $inputs } input and { $outputs ->\n [one] { $outputs } output\n *[other] { $outputs } outputs\n }.\n *[other] Function iterates are possible only if the number of inputs of the function is equal to the number of outputs. This function has { $inputs } inputs and { $outputs ->\n [one] { $outputs } output\n *[other] { $outputs } outputs\n }.\n }\n\n## `<sequence>`\n\nsequence-invalid-length = Invalid length of sequence. Must be a non-negative integer.\n\n# $type is a sequence type: number, letters, or math.\nsequence-invalid-step = Invalid step of sequence. Must be a number for sequence of type { $type }.\n\n# $attribute is `from` or `to` — an attribute name, so it stays in English.\nsequence-invalid-endpoint-number = Invalid "{ $attribute }" of number sequence. Must be a number.\n\nsequence-invalid-endpoint-letters = Invalid "{ $attribute }" of letters sequence. Must be a letter combination.\n\nsequence-invalid-endpoint = Invalid "{ $attribute }" of sequence.\n\nselect-from-sequence-coprime-not-numbers = coprime ignored since not selecting numbers\n\nselect-from-sequence-coprime-with-exclude-combinations = coprime ignored since excludeCombinations specified\n\n## Resolving a `target`\n##\n## Raised by the components that take a `target` attribute. They resolve it\n## through the same code and fail the same two ways, so they share these two\n## messages rather than spelling each one out per component: $source is the tag\n## of the component that raised it, part of the DoenetML language, so it stays\n## in English.\n\ntarget-not-found = Invalid target for `<{ $source }>`: cannot find target.\n\n# $property is the state variable that was looked for; $component is the tag it\n# was looked for on.\ntarget-state-variable-not-found = Invalid target for `<{ $source }>`: cannot find a state variable named "{ $property }" on a `<{ $component }>`.\n\n## `<odeSystem>`\n\node-system-variables-match-independent = Variables of `<odeSystem>` must be different than independent variable.\n\node-system-duplicate-variable-names = Can\'t define ODE RHS functions with duplicate dependent variable names.\n\node-system-rhs-function-error = Cannot define ODE RHS function. Error creating mathjs function.\n\n## `<angle>`, `<parabola>`, and `<intersection>`\n\n# $count is how many line children were found.\nangle-too-many-lines = Cannot define an angle between { $count } lines\n\nangle-invalid-through-point = Invalid point in through of `<angle>`\n\nparabola-vertex-too-many-points = Haven\'t implemented parabola with vertex through more than 1 point.\n\nparabola-too-many-points = Haven\'t implemented parabola through more than 3 points.\n\nintersection-too-many-items = Haven\'t implemented intersection for more than two items\n\n## Other math components\n\nionic-compound-not-two-ions = Have not implemented ionic compound for anything other than two ions.\n\nionic-compound-needs-cation-and-anion = Ionic compound implemented only for one cation and one anion.\n\n# $equation is the equation as the author wrote it.\nsolve-equations-cannot-evaluate = Cannot solve equation as could not evaluate equation: { $equation }\n\n# Translators: `operandNumber` is an attribute name and stays in English.\nmath-operators-operand-number-required = Must specify a operandNumber when extracting a math operand.\n\neigen-decomposition-failed = Could not calculate eigenvalues of matrix\n\n## `<matchesPattern>`\n\n# Translators: `parameters` is an attribute name and stays in English.\n# $parameters lists the parameters as the author wrote them;\n# $parametersCount is how many there were.\nmatches-pattern-parameter-not-in-pattern =\n { $parametersCount ->\n [one] `<matchesPattern>`: the parameter { $parameters } does not occur in the pattern, so it will always match a blank.\n *[other] `<matchesPattern>`: the parameters { $parameters } do not occur in the pattern, so they will always match a blank.\n }\n\n## `<graph>`\n\n# Translators: grid is an attribute name and none, medium and dense are its\n# values; all four stay in English, as does the example. $grid is the value the\n# author wrote, reproduced verbatim — it reached this message precisely because\n# it was not one of the forms listed.\ngraph-grid-invalid = `<graph>`: cannot interpret grid="{ $grid }". It must be none, medium, dense, or two positive numbers separated by a space, such as grid="1 0.5". No grid is drawn.\n\n## `<slopeField>` and `<vectorField>`\n\n# $component is the field\'s tag and $alternative the sibling field component\'s,\n# or `none` when the function would not suit it either; both are DoenetML tags\n# and stay in English, as do the example expressions. $expected is how many\n# outputs the component needs, $found how many the given function has. What a\n# field draws a mark from is the function\'s outputs, so the wrong number of\n# them means there is nothing to draw — and the author almost always wanted the\n# other component.\nfield-function-wrong-num-outputs =\n `<{ $component }>` needs a function with { $expected ->\n [one] one output, the slope y\' at each point, such as `y - x`\n *[other] two outputs, the vector at each point, such as `(y, -x)`\n }, but the function it was given has { $found ->\n [one] { $found } output\n *[other] { $found } outputs\n }. { $alternative ->\n [none] Nothing is drawn.\n *[other] `<{ $alternative }>` is the component for that function. Nothing is drawn.\n }\n\n# Translators: retired. `function` is an attribute name and stays in English.\n# It warned that a field had been given its function both as an attribute and\n# inside the component; the attribute no longer exists, so the two can no\n# longer disagree. Kept because diagnostic codes are never reused.\nfield-function-attribute-ignored-with-child = The `function` attribute is ignored because the function is also given inside the component; the one inside is used. Give the function only one of the two ways.\n\n# Translators: `variables` and `function` are a DoenetML attribute and tag and\n# stay in English, as does $component, the field\'s own tag. The attribute names\n# the inputs of an expression written directly inside the component; when there\n# is no such expression there is nothing for it to name, and $reason says which\n# of the two ways that happened.\nfield-variables-ignored =\n `<{ $component }>`: the `variables` attribute names the variables of an expression written directly inside the component. { $reason ->\n [function-child] The function here is given as a `<function>` child, which names its own variables, so `variables` is ignored.\n *[no-expression] No such expression is given here, so `variables` is ignored.\n }\n\n## PreFigure renderer\n\n# Translators: xLabelPosition, yLabelPosition and their values are attribute\n# names and stay in English, as does the renderer\'s name.\nprefigure-x-label-position-unsupported = `<graph>`: xLabelPosition="left" is not supported in prefigure renderer; using right-position behavior.\n\nprefigure-y-label-position-unsupported = `<graph>`: yLabelPosition="bottom" is not supported in prefigure renderer; using top-position behavior.\n\nprefigure-invalid-axis-bounds = `<graph>`: invalid axis bounds for prefigure conversion; using default bbox (-10,-10,10,10).\n\nprefigure-invalid-width = `<graph>`: invalid width for prefigure conversion; using default diagram width 425.\n\nprefigure-invalid-aspect-ratio = `<graph>`: invalid aspectRatio for prefigure conversion; using default aspect ratio 1.\n\n# Translators: the renderer\'s name, prefigure, stays in English. "grid" here is\n# the coordinate grid itself rather than the attribute that asks for one, so it\n# is prose and is translated.\nprefigure-grid-spacing-too-fine = `<graph>`: the grid spacing is too fine for the axis limits; the grid is omitted in the prefigure renderer.\n\nprefigure-annotations-not-rendered = `<graph>`: annotations will not be rendered when not using the PreFigure renderer.\n\nmultiple-annotations-children = Multiple `<annotations>` children found in `<graph>`; all but the last one are ignored.\n\n## Referring to other components\n##\n## `<updateValue>`\'s own "cannot find target" messages are not here: it\n## resolves a target the same way `<animateFromSequence>` does and fails the\n## same ways, so the two share `target-not-found` and\n## `target-state-variable-not-found` above.\n\ncopy-unrecognized-component-type = Cannot extend or copy an unrecognized component type: { $type }.\n\ncopy-prop-not-found = Could not find prop { $property } on a component of type { $component }\n\ncollect-no-source = No source found for collect.\n\ncollect-invalid-component-type = Cannot collect components of type `<{ $component }>` as it is an invalid component type.\n\n# $reference is the reference exactly as the author wrote it, `$` and all —\n# the `$p.styleDescription[1]` of `<text extend="$p.styleDescription[1]" />`.\n# An index only means something applied to an array, and the thing named here\n# is not one. The reference is quoted back rather than explained because the\n# text in front of the author is the only part of this they can act on: the\n# state variable and component index the core knows about are its own business\n# and go to the console instead.\nreference-index-unavailable = Cannot reference index `{ $reference }`\n\n## `<callAction>`\n\n# $action is the `actionName` the author asked for, part of the DoenetML\n# language, so it stays in English. $reference is the `target` as written.\ncomponent-action-unavailable = Cannot call { $action } on component `{ $reference }`\n\n## `<dataFrame>`\n\n# $componentIdx is an internal index, passed as a string so it is not grouped\n# like a quantity; the odd spacing before the colon is reproduced from the\n# original message.\ndata-frame-inconsistent-row-lengths = Data has invalid shape. Rows has inconsistent lengths. Found in componentIdx :{ $componentIdx }\n\ndata-frame-duplicate-column-names = Data has duplicate column names. Found in componentIdx :{ $componentIdx }\n\ndata-frame-missing-column-name = Data is missing a column name. Found in componentIdx :{ $componentIdx }\n\n## `<answer>` and scoring\n\nanswer-award-depends-on-own-response = An award for this answer is based on the answer tag\'s own submitted response, which will lead to unexpected behavior.\n\n# Translators: maxNumAttempts and sectionWideCheckWork are attribute names.\nanswer-max-num-attempts-in-section-wide-check-work = Setting `maxNumAttempts` on an `<answer>` inside a container with `sectionWideCheckWork` has no effect, as the number of attempts is controlled by the container. Set `maxNumAttempts` on the container instead.\n\nnested-section-wide-check-work-max-num-attempts = Setting `maxNumAttempts` on a container with `sectionWideCheckWork` that is inside another container with `sectionWideCheckWork` has no effect, as the number of attempts is controlled by the outer container. Set `maxNumAttempts` on the outer container instead.\n\n# $attributes is a list of attribute names; $attributesCount is its length.\nanswer-attributes-need-symbolic-equality =\n { $attributesCount ->\n [one] The { $attributes } attribute will have no effect without symbolicEquality set.\n *[other] The { $attributes } attributes will have no effect without symbolicEquality set.\n }\n\nanswer-invalid-type = Invalid type for answer: { $type }\n\n## `<module>`, `<conditionalContent>`, `<slider>`, `<pretzel>`\n\nmodule-attribute-child-needs-name = Since the component `<{ $component }>` does not have a name, it cannot be used for a module attribute\n\nmodule-attribute-name-already-defined = The component `<{ $component } name="{ $name }">` cannot be used as an attribute for a module because the `<module>` component type already has a "{ $name }" attribute defined.\n\nconditional-content-condition-ignored = Attribute `condition` is ignored on a `<conditionalContent>` component with case or else children.\n\nslider-markers-type-mismatch = Markers type doesn\'t match slider type.\n\npretzel-problem-needs-statement-and-answer = Invalid pretzel: each `<problem>` must contain one `<statement>` and one `<answer>`.\n\npretzel-circuit-first-problem-distractor = Invalid pretzel: in mode="circuit", the first `<problem>` cannot be a distractor.\n\n## Attribute values\n\n# $values is a list of the values that were rejected, each already in\n# backticks; $valuesCount is how many there were.\nattribute-invalid-values =\n { $valuesCount ->\n [one] Invalid value { $values } for attribute `{ $attribute }`; ignoring.\n *[other] Invalid values { $values } for attribute `{ $attribute }`; ignoring.\n }\n\nattribute-must-be-references = Invalid value `{ $value }` for attribute `{ $attribute }`. Attribute must be composed of references that begin with a `$`.\n\n# $names is a list of the rejected names, each already in single quotes.\nmath-input-invalid-function-names = <mathInput>: ignored invalid function name(s) in { $attribute }: { $names }. Each name\'s display segment must be at least 2 characters (letters or dashes); an optional `|<mathspeak alternative>` suffix may follow.\n\n## Building components from the source\n\n# Raised while the source is being turned into components, by throwing rather\n# than by building a record: the thrower is caught, the component becomes an\n# `_error`, and the diagnostic is re-raised from it.\n\ncomponent-type-invalid = Invalid component type: `<{ $componentType }>`\n\nattribute-repeated = Cannot repeat attribute { $attribute }.\n\nattribute-invalid-for-component = Invalid attribute "{ $attribute }" for a component of type `<{ $componentType }>`.\n\n## Style definition contrast\n\n# $context names the pair being compared, $mode which colour scheme it was\n# rendered in. Both are symbolic — the phrase is chosen here so a translator\n# can rewrite it, rather than being handed over already in English.\nstyle-definition-insufficient-contrast =\n Style definition { $styleNumber } has insufficient contrast for { $context ->\n [text-on-background] text color against background color\n [high-contrast] high-contrast color against the canvas\n [line] line color against the canvas\n [marker] marker color against the canvas\n *[text-on-canvas] text color against the canvas\n }{ $mode ->\n [dark] { " (dark mode)" }\n *[light] { "" }\n } ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1).\n\n# $suggestion says whether a concrete replacement colour could be computed.\n# The attribute names and colour values in the `available` branch are\n# DoenetML source, not prose, and stay as they are in every language.\nstyle-definition-dark-mode-text-background-contrast =\n Although style definition { $styleNumber } has specified colors that provide sufficient contrast for light mode, the dark-mode colors derived from these values have insufficient contrast for the text color against the background color ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1). { $suggestion ->\n [available] To ensure sufficient contrast in dark mode, either increase the light-mode contrast (e.g., set { $lightAttribute }="{ $lightColor }") or override the dark-mode color (e.g., set { $darkAttribute }="{ $darkColor }").\n *[none] To ensure sufficient contrast in dark mode, increase the light-mode contrast or override the derived colors with textColorDarkMode and/or backgroundColorDarkMode.\n }\n\nstyle-definition-dark-mode-text-canvas-contrast =\n Although style definition { $styleNumber } has a specified text color that provides sufficient contrast for light mode, the dark-mode text color derived from this value has insufficient contrast against the canvas ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1). { $suggestion ->\n [available] To ensure sufficient contrast in dark mode, either increase the light-mode contrast (e.g., set textColor="{ $lightColor }") or override the dark-mode color (e.g., set textColorDarkMode="{ $darkColor }").\n *[none] To ensure sufficient contrast in dark mode, increase the light-mode contrast or override the derived color with textColorDarkMode.\n }\n\nsection-multiple-style-palettes = A section can select only one <stylePalette>; using the last one.\n\n## Unique variants\n\n# Explanations of why a component\'s unique variants could not be worked out.\n# $component is the tag that could not be analyzed and stays as written; the\n# reason is a separate message per situation, so a host can tell them apart by\n# code and a translator sees a whole sentence rather than a fragment.\n\nvariant-num-to-select-not-non-negative-integer = cannot determine unique variants of { $component } as numToSelect isn\'t a non-negative integer.\n\nvariant-num-to-select-not-constant-number = cannot determine unique variants of { $component } as numToSelect isn\'t constant number.\n\nvariant-with-replacement-not-constant-boolean = cannot determine unique variants of { $component } as withReplacement isn\'t constant boolean.\n\nvariant-select-weight-disables-unique = Unique variants for select disabled if have an option with selectWeight or selectForVariants specified\n\nvariant-coprime-undetermined = cannot determine unique variants of { $component } as cannot determine coprime is always false.\n\n# $attribute is an attribute name (`from`, `to`, `step`, `sort`, `length`) and\n# stays as written.\nvariant-attribute-not-constant = cannot determine unique variants of { $component } as { $attribute } isn\'t a constant.\n\nvariant-attribute-not-number = cannot determine unique variants of { $component } as { $attribute } isn\'t a number.\n\n# $type is the sequence type the component was declared with. $expected names\n# what the value had to be, symbolically, because which one applies depends on\n# both the type and the attribute.\nvariant-attribute-wrong-type-for-sequence =\n cannot determine unique variants of { $component } of { $type } type as { $attribute } isn\'t { $expected ->\n [letters-combination] a combination of letters\n [math-expression] a valid math expression\n [integer] an integer\n *[number] a number\n }.\n\nvariant-length-not-integer = cannot determine unique variants of { $component } as length isn\'t an integer.\n\nvariant-sort-not-implemented = have not implemented unique variants of a { $component } with sort\n\nvariant-exclude-combinations-not-implemented = have not implemented unique variants of a { $component } with excludeCombinations\n\nvariant-math-exclude-not-implemented = have not implemented unique variants of a { $component } of type math with exclude\n\nvariant-non-constant-exclude-not-implemented = have not implemented unique variants of a { $component } with non-constant exclude\n\n## PreFigure conversion\n\n# $subject identifies the component the warning is about, already written as\n# `<tag>` or `<tag> (name)`. It is composed in code rather than here because\n# Fluent terms cannot take a variable as an argument, so a shared subject\n# fragment cannot be parameterized from the catalog. It holds only a tag name,\n# a component name and punctuation — never a word — which is why a descendant\n# with no type reads `<?>` rather than `<unknown>`.\n\nprefigure-descendant-unsupported = { $subject }: unsupported in graph prefigure renderer; descendant skipped.\n\nprefigure-descendant-invalid-geometry = { $subject }: non-finite or incomplete geometry; descendant skipped.\n\nprefigure-curve-label-omitted = { $subject }: labels are not supported on converted curve elements; label omitted.\n\nprefigure-curve-unsupported-definition-type = { $subject }: unsupported curve function definition type \'{ $definitionType }\'; descendant skipped.\n\nprefigure-region-flip-functions-unsupported = { $subject }: unsupported flipFunctions attribute on regionBetweenCurves; descendant skipped.\n\nprefigure-region-non-formula-child = { $subject }: only formula-typed child functions are supported on regionBetweenCurves; descendant skipped.\n\n# $labelKind says which family of object carried the label, since the advice\n# is the same but the object is not.\nprefigure-label-position-unsupported =\n { $subject }: unsupported labelPosition \'{ $labelPosition }\' for { $labelKind ->\n [line-family] line-family label\n *[point] point label\n }; default PreFigure alignment used.\n\nprefigure-fill-style-unsupported = { $subject }: fill style \'{ $fillStyle }\' is unsupported by PreFigure; falling back to a solid fill.\n\nprefigure-line-style-unknown = { $subject }: unknown line style \'{ $lineStyle }\' omitted from PreFigure output.\n\nprefigure-marker-style-mapped-to-diamond = { $subject }: marker style \'{ $markerStyle }\' mapped to PreFigure style \'diamond\'.\n\nprefigure-marker-style-unsupported = { $subject }: marker style \'{ $markerStyle }\' is unsupported by PreFigure; default style used.\n\n## PreFigure annotations\n\nannotation-ref-unresolvable = `<annotation>`: invalid `ref`; cannot resolve target. Annotation omitted.\n\nannotation-ref-multiple-targets = `<annotation>`: `ref` resolved to multiple targets; using the first target.\n\nannotation-ref-outside-graph = `<annotation>`: invalid `ref`; target is outside the containing graph. Annotation omitted.\n\nannotation-ref-unsupported-target = `<annotation>`: invalid `ref`; target is not a supported graphical object in prefigure conversion. Annotation omitted.\n\nannotation-text-missing = `<annotation>`: missing or empty `text`; emitting empty text.\n\n## Composites and references\n\n# $componentType is the type the composite was asked to create, when it is\n# known; `none` when the composite did not say.\ncomposite-circular-dependency =\n { $componentType ->\n [none] Circular dependency detected.\n *[other] Circular dependency detected involving `<{ $componentType }>` component.\n }\n\n# $reference is the reference as the author wrote it, already carrying its `$`.\nreference-no-referent = No referent found for reference: `{ $reference }`\n\nreference-multiple-referents = Multiple referents found for reference: `{ $reference }`\n\n## Children that do not match\n\nchildren-invalid-attribute-format = Invalid format for attribute { $attribute } of `<{ $componentType }>`.\n\n# $children is the list of child types that did not match, already joined.\nchildren-invalid = Invalid children for `<{ $componentType }>`: Found invalid children: { $children }\n\n## Falling back to a default\n\n# The attribute had a default to fall back to, and the message names it: the\n# component went on to behave as though the author had written that value, and\n# an author not told which one goes looking for the difference in the wrong\n# place.\nattribute-value-invalid-using-default = Invalid value `{ $value }` for attribute `{ $attribute }`, using value `{ $default }`\n\n# The same rejection where the attribute has no default to fall back to, so\n# nothing stood in for the value and the component behaves as though the\n# attribute had not been written. A separate message rather than the one above\n# with a blank in it: "using value `null`" would describe a substitution that\n# did not happen, and name a value no author could have typed. What follows is\n# usually a second message from the component itself, saying what it did\n# without the attribute.\nattribute-value-invalid-ignoring = Invalid value `{ $value }` for attribute `{ $attribute }`, ignoring it\n\n## Loading a DoenetML version\n\n# $fallback is the version that will be used instead, or `none` when the\n# embedding page named a standalone bundle of its own.\ndoenetml-version-not-found =\n { $fallback ->\n [none] DoenetML version { $version } not found.\n *[other] DoenetML version { $version } not found. Falling back to version { $fallback }\n }\n\n## Reading the DoenetML\n\n# The parser\'s own diagnostics: what the author sees before anything runs, and\n# for a beginner usually the first Doenet message they ever read.\n#\n# The parser writes its English beside the code rather than rendering it from\n# here, because `@doenet/parser` is inside the language-server bundle and a\n# catalog there is dead weight on the editor\'s critical path\n# (`packages/lsp/scripts/check-server-bundle.mjs` fails if one arrives). The\n# two copies are held together by a test in that package, which parses a\n# corpus and asserts every coded error renders to exactly what the parser\n# wrote — so a message edited here without its counterpart fails there.\n#\n# $tag, $value, $attribute and their relatives quote the author\'s own source\n# back at them and stay exactly as written. The `Invalid DoenetML: ` opening is\n# repeated in each message instead of being a shared term: a term a locale\n# forgets to define renders as its own name, and a prefix on fifteen messages\n# is not worth that failure mode.\n\nparse-invalid-doenetml = Invalid DoenetML: { $content }\n\nparse-tag-missing-close-tag = Invalid DoenetML: The tag `{ $tag }` has no closing tag. Expected a self-closing tag or a `</{ $tagName }>` tag.\n\nparse-tag-error = Invalid DoenetML: Error in tag `<{ $tagName }>`\n\nparse-attribute-missing-value = Invalid DoenetML: Invalid attribute `{ $attribute }` appears to be missing a value.\n\nparse-attribute-invalid = Invalid DoenetML: Invalid attribute `{ $attribute }`\n\nparse-attribute-value-invalid = Invalid DoenetML: Invalid attribute value `{ $value }`\n\n# $quote is the quote character that would balance the pair: `"` or `\'`.\nparse-attribute-value-quote-mismatch = Invalid DoenetML: Invalid attribute value `{ $value }`. The quote marks do not match. You appear to be missing a `{ $quote }`\n\nparse-open-tag-name-missing = Invalid DoenetML: Found a tag without a tag name, e.g. `<`\n\nparse-tag-not-closed = Invalid DoenetML: Tag `{ $tag }` was not closed (a `>` appears to be missing).\n\nparse-self-closing-tag-name-missing = Invalid DoenetML: Found a tag without a tag name `<{ $content }>`\n\nparse-self-closing-tag-not-closed = Invalid DoenetML: Tag `{ $tag }` was not closed (`/>` appears to be missing).\n\nparse-tag-invalid-attributes = Invalid DoenetML: Tag `{ $tag }` is not valid. It may have incorrect attributes.\n\nparse-close-tag-name-missing = Invalid DoenetML: Found a closing tag without a tag name, e.g. `</`\n\n# $attribute is the attribute name and $value the unquoted token that followed\n# it, shown reassembled the way the author should have written it.\nparse-attribute-value-unquoted = Attribute values must be enclosed in quotes: `{ $attribute }="{ $value }"`\n\nparse-close-tag-without-open-tag = Invalid DoenetML: Found closing tag `{ $tag }`, but no corresponding opening tag\n\nparse-close-tag-mismatched = Invalid DoenetML: Mismatched closing tag. Expected `</{ $expected }>`. Found `{ $found }`\n\n# The conversion\'s fall-through: the syntax tree held a node shape it has no\n# case for. Reaching an author means the grammar and the conversion have gone\n# out of step, which is a bug in Doenet rather than in their document — hence\n# `parser-` rather than the `parse-` of every message above, which is about\n# what the author wrote. They are still the one looking at it, so it is\n# translated like any other error. $node is the node\'s own name and stays as\n# it is.\nparser-node-unconvertible = Could not convert node { $node } to Dast node.\n\n## Names\n\n# $reason says which rule the name broke, as a key rather than a phrase, so the\n# whole sentence is translatable rather than assembled from two halves.\nname-attribute-invalid =\n Invalid attribute name=\'{ $name }\'. { $reason ->\n [characters] Names can contain only letters, numbers, underscores or hyphens.\n *[start] Names must start with a letter.\n }\n\ncomponent-name-invalid-start = Invalid component name "{ $name }". Names must start with a letter.\n\n## `<answer>` sugar\n\nanswer-video-watched-missing-video = Answer with type videoWatched must have a video attribute\n\nanswer-video-watched-video-not-reference = Answer with type videoWatched must have video attribute that is a reference\n\nanswer-name-not-single-text = Answer name attribute must have a single text child\n\n## Referencing another document\n\n# $attribute is the attribute the URI was written in (`copyFrom`, `extend`, …)\n# and stays as written; $uri is the author\'s own value.\n\nexternal-doenetml-recursion-limit = Unable to retrieve external DoenetML due to too many levels of recursion. Is there a circular reference?\n\nexternal-doenetml-unavailable = Unable to retrieve DoenetML from { $attribute }="{ $uri }"\n\nexternal-doenetml-type-mismatch = Invalid DoenetML retrieved from { $attribute }="{ $uri }": it did not match the component type "{ $componentType }"\n\n## Deprecated syntax\n\n# $from and $to are attribute names and $component a tag name; all three stay\n# as written. $component is `none` for a rename that applies to every component\n# accepting the attribute, where naming one would be wrong.\n#\n# The `[deprecation]` opening is a literal marker shared by all four messages,\n# not a word: leave it as it is.\n\ndeprecated-attribute-renamed =\n { $component ->\n [none] [deprecation] Attribute `{ $from }` is deprecated; use `{ $to }` instead.\n *[other] [deprecation] Attribute `{ $from }` on `<{ $component }>` is deprecated; use `{ $to }` instead.\n }\n\ndeprecated-attribute-renamed-conflict =\n { $component ->\n [none] [deprecation] Attribute `{ $from }` is deprecated and ignored because `{ $to }` is also specified.\n *[other] [deprecation] Attribute `{ $from }` on `<{ $component }>` is deprecated and ignored because `{ $to }` is also specified.\n }\n\ndeprecated-attribute-ignored = [deprecation] Attribute `{ $attribute }` on `<{ $component }>` is deprecated and ignored.\n\n# An attribute replaced by a child element rather than by another attribute:\n# $attribute and $component stay as written, and so does $child, which is the\n# tag name of the child to write instead.\n\ndeprecated-attribute-to-child = [deprecation] Attribute `{ $attribute }` on `<{ $component }>` is deprecated; use a `<{ $child }>` child instead.\n\n# One value of an attribute is deprecated while the attribute itself stays:\n# $value is what the author wrote and $to what to write instead, with\n# $attribute and $component naming where. All four stay as written.\n\ndeprecated-attribute-value-renamed = [deprecation] Value `{ $value }` of attribute `{ $attribute }` on `<{ $component }>` is deprecated; use `{ $to }` instead.\n\n\n## Language coverage\n\n# `<pluralize>` runs an English part-of-speech model over its text and puts the\n# nouns in the plural. There is no equivalent for an arbitrary language — a\n# correct plural needs that language\'s own morphology, and often its\n# dictionary — so in a document written in anything else the word is left as\n# the author typed it, and this says so rather than silently doing nothing.\n#\n# Raised only when there is nothing else to fall back on: a `pluralForm` is the\n# author supplying their own language\'s plural, and that is honored in every\n# language, which is why this can recommend it.\n#\n# $locale is the document\'s language tag, as declared.\npluralize-english-only = `<pluralize>` can only pluralize English, so its text is left unchanged in a document written in { $locale }. Write the plural form directly, or set it with the `pluralForm` attribute.\n\n\n## Checking against the schema\n\n# What the editor draws a squiggle under: the language server\'s own check of\n# the document against the DoenetML schema, run on every keystroke and\n# answered before anything is evaluated. A beginner meets these first, and\n# usually only these, because a document that does not pass them rarely gets\n# as far as producing a diagnostic from the core.\n#\n# `@doenet/lsp-tools` writes its English beside the code rather than rendering\n# it from here, for the same reason `@doenet/parser` does: it is inside the\n# language-server bundle, which `@doenet/codemirror` embeds verbatim and\n# starts as a blob worker, and a catalog on the editor\'s critical path is what\n# `packages/lsp/scripts/check-server-bundle.mjs` exists to reject. The two\n# copies are held together by a test in that package, which runs the checker\n# over a corpus and asserts every coded violation renders to exactly what the\n# checker wrote — so a message edited here without its counterpart fails\n# there.\n#\n# $tag, $parent and $attribute quote the author\'s own source back at them and\n# stay exactly as written; the angle brackets and backticks around them are\n# punctuation this catalog supplies, not part of the name.\n\nschema-element-unrecognized = Element `<{ $tag }>` is not a recognized Doenet element.\n\nschema-element-not-allowed-at-root = Element `<{ $tag }>` is not allowed at the root of the document.\n\nschema-element-not-allowed-inside = Element `<{ $tag }>` is not allowed inside of `<{ $parent }>`.\n\nschema-attribute-unrecognized = Element `<{ $tag }>` doesn\'t have an attribute called `{ $attribute }`.\n\n# $allowed is the attribute\'s permitted values, each already in double quotes\n# and joined for the reader\'s language. $isList says whether the attribute\n# takes several of them at once: one situation, two sentences, because the\n# reader has to be told they are choosing a whole list rather than a value.\nschema-attribute-value-not-allowed =\n { $isList ->\n [true] Attribute `{ $attribute }` of element `<{ $tag }>` must be a list whose items are each one of: { $allowed }\n *[other] Attribute `{ $attribute }` of element `<{ $tag }>` must be one of: { $allowed }\n }\n\n\n## The `<select>` family\'s error boxes\n##\n## The messages that replace the whole component with a red box rather than\n## warning beside it: nothing can be selected, so there is nothing to render.\n## They were the last uncoded `_error` path in the worker (#1581).\n##\n## Counts arrive as numbers rather than as text, so a language that agrees a\n## noun with them can select on them. English does not agree here — it says\n## "1 options" today and these messages reproduce it exactly, because the box\'s\n## text is what the existing suites pin.\n##\n## Being numbers, they are grouped: a count of 1500 renders as "1,500" in\n## English where the concatenated sentence these replaced wrote "1500". That is\n## the one way the English moved, and it moved in the right direction — these\n## are quantities, not identifiers like a line or a section number, which is\n## the distinction that decides between a number and a string everywhere in\n## these catalogs.\n##\n## Translators: component and attribute names — `selectFromSequence`,\n## `selectPrimeNumbers`, `from`, `to`, `step` — are DoenetML identifiers, not\n## words. They are written into these messages as they stand and must be left\n## in English exactly as written.\n\nselect-variant-name-option-count-mismatch = Invalid variant name for select. Variant name { $variantName } appears in { $numOptions } options but number to select is { $numToSelect }.\n\nselect-variant-name-without-options = Some variants are specified for select but no options are specified for possible variant name: { $variantName }.\n\nselect-variant-name-not-possible = Variant name { $variantName } that is specified for select is not a possible variant name.\n\nselect-too-few-options = Cannot select { $numToSelect } components from only { $numOptions }.\n\nselect-from-sequence-too-few-values = Cannot select { $numToSelect } values from a sequence of length { $length }.\n\nselect-from-sequence-indices-count-mismatch = Number of indices specified for select must match number to select\n\nselect-from-sequence-indices-not-integers = All indices specified for select must be integers\n\nselect-from-sequence-index-excluded = Specified index of selectfromsequence that was excluded\n\nselect-from-sequence-indices-excluded-combination = Specified indices of selectfromsequence that was an excluded combination\n\nselect-from-sequence-coprime-not-positive-integers = Cannot select coprime combinations as not selecting positive integers.\n\n# Translators: from, to and step are attribute names. They are written into\n# the message rather than passed in because these three never vary — an\n# argument is for a name that changes from one call to the next.\nselect-from-sequence-coprime-common-factor = Cannot select coprime numbers. All possible values share a common factor. (Specified values of "from" or "to" must be coprime with "step".)\n\nselect-from-sequence-coprime-single-number = Cannot select coprime combinations from a single number that is not 1.\n\nselect-from-sequence-excluded-too-many-combinations = Excluded over 70% of combinations in selectFromSequence\n\n# The sibling of `select-from-sequence-coprime-common-factor`, and a different\n# situation rather than a rewording of it: that one is decided up front, from\n# the sequence\'s own arithmetic. This one is what is left after two hundred\n# draws found no coprime combination among values that could have supplied one.\nselect-from-sequence-coprime-none-found = Could not select coprime numbers. All possible values share a common factor.\n\nselect-from-sequence-too-few-unique-values = Cannot select { $numToSelect } unique values from sequence of length { $numPossibleValues }\n\nselect-prime-numbers-too-few-values = Cannot select { $numToSelect } values from a list of primes of length { $numValues }\n\nselect-prime-numbers-values-count-mismatch = Number of values specified for select must match number to select\n\nselect-prime-numbers-values-not-prime = All values specified for select prime number must be in the list of primes\n\nselect-prime-numbers-values-excluded-combination = Specified values of selectPrimeNumbers was an excluded combination\n\nselect-prime-numbers-excluded-too-many-combinations = Excluded over 70% of combinations in selectPrimeNumbers\n\n# Both flukes are shared by `<selectFromSequence>` and `<selectPrimeNumbers>`,\n# which say the same thing in the same words. Neither is reachable in practice\n# — each follows two hundred independent draws — but an unreachable box is\n# still a box, and it renders in whatever language the rest of the page does.\nselect-random-combination-fluke = By extremely unlikely fluke, couldn\'t select combination of random values\n\nselect-random-value-fluke = By extremely unlikely fluke, couldn\'t select random value\n\n## Inputs embedded in math\n\n# Translators: $component is the input\'s own DoenetML tag and stays in English,\n# as do the attribute names `inline`, `expanded` and `width`. An input written\n# inside `<m>` is drawn in the place it is written, inside the typeset\n# expression, which makes room for it as it grows. That needs a control small\n# enough to sit in a line of mathematics, with a width the expression can\n# measure, in an expression that is not drawn on a graph; $reason says which of\n# those this input fails. When it fails, the input is not shown at all, and the\n# expression is typeset with the input\'s value in its place — a text input\'s\n# text, a math input\'s mathematics — or with nothing there for a choice input.\nmath-embedded-input-shape-unsuitable =\n { $reason ->\n [not-inline] This `<{ $component }>` is not shown because it is inside math and is not `inline`. Add `inline` so it becomes a drop-down list, which fits inside an expression.\n [expanded] This `<{ $component }>` is not shown because it is inside math and is `expanded`. Remove `expanded`; a multi-line box does not fit inside an expression.\n [on-graph] This `<{ $component }>` is not shown because it is inside math drawn on a graph, which has no room for an input.\n *[relative-width] This `<{ $component }>` is not shown because it is inside math and has a relative width. Give the width in absolute units, such as `px`, instead.\n }\n\n## `<sampleRandomNumbers>` and `<selectRandomNumbers>`\n##\n## Translators: `mean`, `standardDeviation`, `variance`, `numTotal`,\n## `numSuccesses`, `numDraws`, `numTrials`, `probability` and `numSamples` are\n## DoenetML attribute names. They are written into these messages as they stand\n## and must be left in English exactly as written. Each of these names the\n## attribute the author has to change, so a message that translated one would\n## point at an attribute that does not exist.\n\n# The spread reaches the sampler as a standard deviation however it was written,\n# so a negative `variance` arrives here as a `standardDeviation` of NaN and the\n# message has to name both for the author to know what to edit.\nsample-gaussian-parameters-invalid =\n Invalid mean ({ $mean }) or standardDeviation ({ $standardDeviation }) for a gaussian random variable. The mean must be a finite number, and the standardDeviation (or the variance it is derived from) must be finite and non-negative. No numbers can be sampled.\n\n# These three attributes are the only ones with no default, so leaving one out is\n# the commonest way to reach this message. Each arrives either as the number the\n# author wrote or as `not-set` for an attribute they left off entirely; translate\n# the "not set" wording, but leave the `not-set` key that selects it untouched.\nsample-hypergeometric-parameters-invalid =\n Invalid numTotal ({ $numTotal ->\n [not-set] not set\n *[other] { $numTotal }\n }), numSuccesses ({ $numSuccesses ->\n [not-set] not set\n *[other] { $numSuccesses }\n }), or numDraws ({ $numDraws ->\n [not-set] not set\n *[other] { $numDraws }\n }) for a hypergeometric random variable. numTotal must be a positive whole number, and numSuccesses and numDraws must be non-negative whole numbers no larger than numTotal. All three must also stay below about nine quadrillion, past which whole numbers can no longer be counted exactly.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\nsample-hypergeometric-draws-too-many =\n Drawing { $numDraws } items from a population of { $numTotal } would need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce numDraws, or bring it closer to numTotal.\n\nsample-binomial-parameters-invalid =\n Invalid numTrials ({ $numTrials }) or probability ({ $probability }) for a binomial random variable. numTrials must be a non-negative whole number below about nine quadrillion, past which whole numbers can no longer be counted exactly, and probability must be between 0 and 1.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\nsample-binomial-trials-too-many =\n Running { $numTrials } trials would need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce numTrials.\n\nsample-poisson-mean-invalid =\n Invalid mean ({ $mean }) for a poisson random variable. The mean must be a finite, non-negative number.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\nsample-poisson-mean-too-large =\n A poisson mean of { $mean } would need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce the mean.\n\n# $distribution names the distribution being sampled, e.g. "binomial" or\n# "multivariate hypergeometric"; $draws is roughly how many random draws each value\n# needs. Raised by `<sampleRandomNumbers>`, `<selectRandomNumbers>` and\n# `<sampleMultivariateRandomNumber>`, which count values with `numSamples`,\n# `numToSelect` and one vector respectively, so the wording names no attribute.\nsample-distribution-slow =\n Each value from this { $distribution } distribution needs about { $draws } random draws, so sampling may be slow. Reduce the distribution\'s parameters, or ask for fewer values, if the page feels sluggish.\n\n## List index operators\n\n# Raised by the index-returning list operators (`<indexOf>`, `<searchSorted>`)\n# when no `target` was given. $component names the tag the author wrote.\nindex-operator-missing-target =\n `{ $component }` has no `target` to look for, so it cannot report a position and gives 0.\n\n# Raised when the operator had no values to look through — an empty list, or one\n# whose only children were references that produced nothing.\nindex-operator-no-values =\n `{ $component }` has no values to look through, so it gives 0, which is not the index of any item.\n## `<sampleMultivariateRandomNumber>`\n##\n## Translators: `type`, `numInCategories` and `numDraws` are DoenetML attribute\n## names, and `hypergeometric` is one of the values `type` takes. They are written\n## into these messages as they stand and must be left in English exactly as\n## written. Each names the attribute the author has to change, so a message that\n## translated one would point at an attribute that does not exist.\n\n# Neither attribute has a default that names a population — numDraws has none at\n# all, and numInCategories falls back to a list with no categories in it — so\n# leaving one out is the commonest way to reach this message. Each arrives either\n# as the value the author wrote or as `not-set`: for numDraws when it is missing,\n# and for numInCategories when it names no categories, whether it was left off or\n# written empty. Translate the "not set" wording, but leave the `not-set` key that\n# selects it untouched.\nsample-multivariate-parameters-invalid =\n Invalid numInCategories ({ $numInCategories ->\n [not-set] not set\n *[other] { $numInCategories }\n }) or numDraws ({ $numDraws ->\n [not-set] not set\n *[other] { $numDraws }\n }) for a multivariate hypergeometric random variable. numInCategories must list at least one category, each a non-negative whole number, and numDraws must be a non-negative whole number no larger than their total. Each category, that total, and numDraws must also stay below about nine quadrillion, past which whole numbers can no longer be counted exactly.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\n# Drawing nearly the whole population is as cheap as drawing almost none of it, so\n# raising numDraws is a fix as well as lowering it. "could need" rather than "would\n# need": each category is counted at the most its draw could cost, so the count\n# these parameters are refused on is an upper bound on the work they really take.\nsample-multivariate-draws-too-many =\n Drawing { $numDraws } items from a population of { $numTotal } split into { $numCategories } categories could need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce numDraws, bring it closer to numTotal, or use fewer categories.\n\n# `type` has no default, so this is what leaving it off gets. A type the attribute\n# does not recognize falls back to no type at all and reaches this message too,\n# after a separate one naming the value that was rejected — hence "no distribution\n# was named" rather than wording that assumes the attribute is missing.\nsample-multivariate-type-not-specified =\n No multivariate distribution was named for this random variable, so nothing was sampled. Give the type attribute the name of a distribution, such as `type="hypergeometric"`.\n\n## Counting operators\n\n# Raised by `<tally>` when the author named `categories` explicitly and some\n# values matched none of them, so those values were not counted anywhere. Info\n# rather than a warning: a list fed by an input legitimately holds\n# non-categories while a student is typing. It deliberately does not say how\n# many such values there were: the diagnostics queue is append-only and\n# deduplicates by message, so a count would leave one permanent entry behind\n# for every number of stray values the list passed through while being typed.\ntally-values-outside-categories =\n Some values matched none of the declared categories, so they were not counted.\n\n# Raised by `<tally>` when `categories` names the same category more than once.\n# A warning rather than info: unlike a stray value, a repeated category is a\n# property of what the author wrote, so it stays true of a settled document. The\n# message says what the counts will be, since the behavior is defensible but not\n# what an author who repeated a category by accident expects.\ntally-repeated-category =\n `categories` names the same category more than once. Each count reports the values equal to its own category, so the repeated ones all report the same number.\n\n# Raised by `<binCounts>` when no `bins` attribute was given, so there are no\n# intervals to count into. $component names the tag the author wrote.\nbin-counts-missing-bins =\n `{ $component }` has no `bins` cut points, so there are no intervals to count into and it gives no counts.\n\n# Raised by `<binCounts>` when `bins` held fewer than the two cut points needed\n# to define a single interval. $count is how many were given, and can be 0 or 1,\n# so the sentence is worded to read for both without forking on the number.\nbin-counts-too-few-cut-points =\n `bins` needs at least 2 cut points to define an interval, but received { $count }.\n\n# Raised by `<binCounts>` when its values are not all numeric, so they cannot be\n# placed between numeric cut points. $component names the tag the author wrote.\nbin-counts-values-not-numeric =\n `{ $component }` can only count numeric values into bins, but not every value it was given is numeric. All counts are 0.\n\n# Raised by `<binCounts>` when the `bins` cut points do not climb: one is\n# smaller than the one before it, so a bin would run backwards and its count\n# would come out negative, or one is not a number at all, which has the same\n# effect. Equal adjacent cut points are accepted, so the message says "at least\n# as large as" rather than "increasing": what is rejected is a decrease, not a\n# failure to strictly increase. Between two interior cut points that names an\n# empty bin; at either end the outermost-edge rule can still put a value in it.\nbin-counts-cut-points-decreasing =\n Each `bins` cut point must be a number at least as large as the one before it, but one is not a number or is smaller than its predecessor, so no counts were produced.\n\n## Charts\n\n# Raised by `<chart type="bar">` when a value is not a finite number, so it gets\n# no bar. The alternative reading of a missing bar is a value of zero, which the\n# author cannot distinguish by looking, so the absence is stated rather than left\n# to be inferred. No count: the queue is append-only and deduplicates by message,\n# and the number of such values changes as an input is typed into. (The message\n# key is `bar-chart-…` because a code names one situation forever and this one\n# was issued while the component was still spelled `<barChart>`.)\nbar-chart-values-not-drawable =\n A bar chart draws no bar for a value that is not a finite number, so those places on the chart are empty rather than zero.\n\n# Raised by `<chart type="bar">` when `barWidth` is outside the (0, 1] range a\n# fraction of a bar\'s slot can take. $barWidth is what the author wrote.\nbar-chart-bar-width-invalid =\n `barWidth` must be greater than 0 and at most 1, but { $barWidth } was given. Using 0.8 instead.\n\n# `type` has no default, so this is what `<chart>` on its own gets. A type the\n# attribute does not recognize falls back to no type at all and reaches this\n# message too, after a separate one naming the value that was rejected — hence\n# "no chart type was named" rather than wording that assumes the attribute is\n# missing.\nchart-type-not-specified =\n No chart type was named, so nothing was drawn. Give the type attribute the name of a chart, such as `type="bar"`.\n\n# Raised by `<chart>` when values are written beside its `<series>` children.\n# A value outside every series belongs to no group of the data, so there is no\n# place on the chart to draw it; saying so matters because the alternative\n# reading of the missing bar is that the value was zero.\nchart-values-outside-series =\n A chart with `<series>` children draws only the values inside them, so values written beside a series were not drawn. Move them into a series of their own.\n\n# Raised by `<chart type="line">` and `<chart type="scatter">` when a point\n# cannot be placed. Two situations reach it and the message covers both: a value\n# that is not a finite number, and a value with no `x` beside it — which is what\n# a series given fewer horizontal coordinates than values produces from the\n# position they run out. Neither is distinguishable by looking from having asked\n# for fewer points. No count: the queue deduplicates by message, and the number\n# changes as an input is typed into.\nchart-points-not-drawable =\n A point needs a finite number for both coordinates, so values with no coordinate beside them, and coordinates with no value, were not drawn.\n\n# Raised by `<chart type="pie">` for a value that is not a finite number. The\n# bar chart\'s message says the place on the chart is empty rather than zero; a\n# pie has no places, so this says the slice is missing instead. No count, for\n# the reason the others give: the queue deduplicates by message.\nchart-pie-values-not-drawable =\n A pie chart draws no slice for a value that is not a finite number, and leaves it out of the total.\n\n# Raised by `<chart type="pie">` for a negative value. Its own message rather\n# than the one above, because the reason is different in kind: a negative bar\n# hangs below the baseline it is measured from, and a pie has no baseline to\n# hang anything from, so a negative value is not merely undrawable but has no\n# reading as a share of a total. It is left out of the total as well, so the\n# slices that are drawn are shares of what was actually charted.\nchart-pie-negative-values =\n A slice is a share of a total, so a pie chart draws no slice for a negative value and leaves it out of the total.\n\n# Raised by `<chart type="pie">` when the values that could be shares of a\n# total came to zero. Nothing is drawn: there is no share to take of zero, and\n# dividing by it would put `NaN` in every angle. Not raised for a pie with no\n# values at all, which is one being written rather than one that is wrong, nor\n# for one whose every value was rejected — those already have a message naming\n# the reason.\nchart-pie-total-not-positive =\n The values of this pie chart total zero, so there are no shares to draw. A pie needs at least one value above zero.\n\n# Raised by `<chart type="pie">` holding more than one `<series>` that is not\n# hidden — a hidden one is dropped before the count, like any drawn child of a\n# container that is hidden on its own account. Concentric\n# rings are not a standard chart and a donut is a styling variant rather than a\n# second group, so there is nowhere on a pie for a second series to go.\nchart-pie-one-series =\n A pie chart draws one series, so only the first was drawn. Chart the others separately, or use a type that draws several series at once.\n\n# Raised by `<chart type="pie">` whose `<xLabel>` or `<yLabel>` child has text\n# in it; a blank one asks for nothing and is not reported. A pie\n# has no axes for either to name, so the text is authored prose with nowhere on\n# the page to go — which is worth a message where an ignored *attribute* is not,\n# because what was dropped is something the author wrote for a reader to see.\nchart-pie-axis-name-ignored =\n A pie chart has no axes, so an `<xLabel>` or `<yLabel>` was not drawn. Put the text in a `<title>` instead, or in prose beside the chart.\n\n# Raised by `<chart type="box">` for an observation that is not a finite\n# number. Left out of the summary rather than read as zero, which would move\n# every quartile of the box drawn from it. No count, for the reason the others\n# give: the queue deduplicates by message, and the number changes as an input is\n# typed into.\nchart-box-values-not-drawable =\n A box plot summarizes finite numbers, so an observation that is not one was left out of its summary.\n\n# Raised by `<chart type="box">` carrying a `categories` attribute. A box chart\n# has no categories: each of its series is one position on the axis and is named\n# by its own `<label>`, so the names in `categories` are authored text with\n# nowhere on the page to go — which is worth a message where an ignored\n# *attribute* is not, because what was dropped is something the author wrote for\n# a reader to see.\nchart-box-categories-ignored =\n A box plot names each of its boxes after the series it was drawn from, so `categories` was not used. Give each `<series>` a `<label>` instead.\n';
|
|
38666
|
+
const content = '# Worker-generated content: style descriptions ("thick red line"), boolean\n# words, and other prose the core computes into the document. Selected by\n# `documentLocale`, which follows the content\'s language rather than the\n# reader\'s UI language.\n#\n# Message ids are lower-kebab-case Fluent identifiers, optionally with a\n# single `.attribute` suffix (`color.blue`, `noun.line-segment`).\n\n\n## Style vocabulary\n##\n## The words the style pipeline derives from a component\'s numeric and\n## enumerated style values. A word an author writes directly — `lineColorWord`,\n## `markerStyleWord`, and their siblings — passes through untranslated: the\n## author chose those words, and rewriting them would be a surprise. So does a\n## CSS named color asked for by name ("rebeccapurple"), which\n## `resolveColorWord` deliberately preserves.\n##\n## Every adjective here is handed `$gender`, the grammatical gender of the noun\n## it describes (see `noun-gender`), and `$role`, the syntactic position the\n## phrase it belongs to is going into. English has no agreement and ignores\n## both; a language that inflects selects on them.\n##\n## `$role` is one of:\n##\n## standalone the phrase stands on its own, which is where all but\n## the three below are\n## border-clause inside `style-border-clause`, behind its preposition\n## background-clause the background colour inside `style-text`, behind its\n## preposition\n## text-clause the text colour beside it, inside `style-text`\n##\n## The last three exist because those words are rendered in two places each —\n## once alone, as `borderStyleDescription`, `backgroundColor` and `textColor`\n## report them, and once embedded in a clause. A language that inflects for\n## case needs a different form in each, and no amount of `$gender` can say\n## which is wanted. Which case a position governs is the catalog\'s business:\n## `border-clause` is a dative in German, an instrumental in Russian and\n## Polish, and nothing at all in a language that does not inflect.\n\n# The canonical color families a color value resolves to.\ncolor =\n .black = black\n .white = white\n .gray = gray\n .red = red\n .orange = orange\n .yellow = yellow\n .green = green\n .cyan = cyan\n .blue = blue\n .purple = purple\n .pink = pink\n .brown = brown\n\n# Stroke widths. Only the extremes are named — a middling width is described by\n# its color alone.\nline-width =\n .thick = thick\n .thin = thin\n\n# Dash patterns. A solid stroke is described by its color alone.\nline-style =\n .dashed = dashed\n .dotted = dotted\n\n# Patterns a shape\'s interior can be filled with. A solid fill is described by\n# its color alone.\nfill-style =\n .horizontal = horizontal lines\n .vertical = vertical lines\n .diagonal = diagonal lines\n .backdiagonal = reverse diagonal lines\n .dots = dots\n .diamonds = diamonds\n\n# The things being described. The shapes a point can be drawn as ("square",\n# "cross") are nouns too: a point\'s description names its marker shape rather\n# than always saying "point".\nnoun =\n .line = line\n .line-segment = line segment\n .ray = ray\n .vector = vector\n .curve = curve\n .function = function\n .slope-field = slope field\n .vector-field = vector field\n .parabola = parabola\n .polyline = polyline\n .polygon = polygon\n .triangle = triangle\n .rectangle = rectangle\n .circle = circle\n .region = region\n .point = point\n .square = square\n .diamond = diamond\n .cross = cross\n .plus = plus\n\n# A regular polygon names its side count, so it is a message of its own rather\n# than one of `noun`\'s attributes.\n#\n# `$part` splits the noun where a language needs it split: `head` is the word\n# the adjectives attach to, `tail` a complement that follows them. English\n# folds the side count into the head and has no tail; Spanish says "polígono\n# regular" and puts "de 5 lados" after the adjectives, so that they stay beside\n# the noun they agree with. `style-with-noun` and `style-filled-with-noun`\n# place the two halves.\n#\n# `$numSides` is a real number, so it is formatted by the locale\'s own rules —\n# a 1000-gon reads "1,000-sided" here and "de 1000 lados" in Spanish. That is\n# the number-formatting policy in the README, and the one place a description\n# is not character-for-character what the pre-catalog code produced.\nnoun-regular-polygon =\n { $part ->\n [tail] { "" }\n *[head] { $numSides }-sided regular polygon\n }\n\n# The grammatical gender of the noun being described, passed to every adjective\n# describing it so that translations can agree. English has no grammatical\n# gender, so every noun answers the same and the answer goes unused.\n#\n# `$noun` is one of `noun`\'s attribute names, `regular-polygon` for the shape\n# `noun-regular-polygon` names, or the head of a phrase the description builds\n# without naming it as a noun: `border`, `fill`, `text`, or `background`. A\n# word this message does not list falls to its default gender — which is also\n# what an author\'s own `markerStyleWord` gets, since the catalog has never seen\n# it.\nnoun-gender = neuter\n\n\n## Style composition\n##\n## `$parts` names which pieces the style actually supplies, so that a\n## translation can order and inflect each combination on its own terms instead\n## of substituting into a fixed English frame. An absent piece is a different\n## branch, never an empty placeable.\n\n# The adjectives describing a stroke: its width, its dash pattern, and its\n# color. Also describes a shape\'s border, where the color is dropped when it\n# matches the fill it surrounds.\nstyle-stroke =\n { $parts ->\n [width-style-color] { $width } { $lineStyle } { $color }\n [width-color] { $width } { $color }\n [style-color] { $lineStyle } { $color }\n [width-style] { $width } { $lineStyle }\n [width] { $width }\n [style] { $lineStyle }\n *[color] { $color }\n }\n\n# A style description followed by what it describes: "thick red line".\n#\n# `$nounTail` is the noun\'s trailing complement, for the nouns whose\n# translation splits around the adjectives (see `noun-regular-polygon`).\n# English has none today, so it only ever selects `noun` for itself — the other\n# variant is still what a partly-translated locale falls back to, and dropping\n# it would drop that locale\'s side count.\n#\n# The phrase it builds is never governed by anything, so its `$role` is always\n# `standalone`.\nstyle-with-noun =\n { $parts ->\n [noun-tail] { $description } { $noun } { $nounTail }\n *[noun] { $description } { $noun }\n }\n\n# The word marking a shape as filled.\n#\n# A key of its own, looked up by the code and handed to the messages below as\n# `$filled`, rather than literal text inside them: a language that inflects it\n# has to agree it with the shape. Referencing it from those messages would not\n# do — a term reference (`{ -filled }`) gets an empty scope and never sees\n# `$gender`, and a message reference resolves only inside its own bundle, so a\n# locale that translated `style-filled` but not this word would render the\n# reference literally instead of falling back to English.\n#\n# Said only of the shape itself, so its `$role` is always `standalone`.\nstyle-filled-word = filled\n\n# A filled shape, and the pattern its interior is drawn with, if any.\nstyle-filled =\n { $parts ->\n [pattern] { $filled } { $color } with { $pattern }\n *[plain] { $filled } { $color }\n }\n\n# The same, naming the shape: "filled blue circle with diamonds".\n#\n# The `-tail` variants carry the noun\'s trailing complement, as\n# `style-with-noun` does.\nstyle-filled-with-noun =\n { $parts ->\n [pattern] { $filled } { $color } { $noun } with { $pattern }\n [plain-tail] { $filled } { $color } { $noun } { $nounTail }\n [pattern-tail] { $filled } { $color } { $noun } { $nounTail } with { $pattern }\n *[plain] { $filled } { $color } { $noun }\n }\n\n# The border clause appended to a filled shape: "with a thick red border".\n#\n# `$parts` carries two distinctions English cares about: whether a fill pattern\n# was already mentioned, which makes this a further clause ("and") rather than\n# the first ("with"), and whether the surrounding description named the shape,\n# which is where English wants an article.\nstyle-border-clause =\n { $parts ->\n [with-article] with a { $border } border\n [and] and { $border } border\n [and-article] and a { $border } border\n *[with] with { $border } border\n }\n\n# How a shape\'s interior is filled, on its own: "blue diamonds".\nstyle-fill =\n { $parts ->\n [pattern] { $color } { $pattern }\n *[plain] { $color }\n }\n\nstyle-unfilled = unfilled\n\n# How a piece of text is styled: its color, and the background behind it.\n#\n# The only composition message with no `$role`: its two words sit in two\n# different positions, so they arrive already inflected for `text-clause` and\n# `background-clause` respectively and no one token would describe them both.\nstyle-text =\n { $parts ->\n [background] { $color } with a { $background } background\n *[plain] { $color }\n }\n\n# What `backgroundColor` answers when nothing is drawn behind the text.\nstyle-background-none = none\n\n\n## Boolean words\n##\n## What a `<boolean>` or `<booleanInput>` *displays*.\n##\n## Not what it parses or serializes. `true` and `false` are DoenetML syntax —\n## an author writes them in the source, `<award>` compares against them, and\n## saved state stores them — so they stay English everywhere, in every\n## language, exactly as `<` stays `<`. Only the word the reader sees moves.\n##\n## The consequence is that a value has two spellings in a translated document,\n## and both have to be accepted where one is read back in: see\n## `booleanFromWord` in the worker.\n\nboolean-true = true\nboolean-false = false\n\n\n## Answer buttons\n##\n## The default text on an answer\'s submit button. Only the *default* is\n## translated: an author who writes `submitLabel="Ready?"` gets "Ready?" in\n## every locale, because they chose those words for their document and a\n## translation of it is not Doenet\'s to make.\n\n# Shown when the answer will report whether the response was right.\nanswer-submit-label = Check Work\n\n# Shown when it will not — the reader submits, and is told nothing more.\nanswer-submit-label-no-correctness = Submit Response\n\n\n## Sectional blocks\n##\n## The word a sectional block calls itself, which the reader sees in its\n## heading and on a `<solution>`\'s reveal control. Keyed by the element the\n## author writes, so `<subsection>` and `<subsubsection>` share `.section` —\n## they are all called a section.\n##\n## An author who writes `renameTo` has named the block themselves, and that\n## name passes through in every language.\n\nsection-name =\n .activity = Activity\n .aside = Aside\n .cascade = Cascade\n .definition = Definition\n .example = Example\n .exercise = Exercise\n .exercises = Exercises\n .given-answer = Answer\n .note = Note\n .objectives = Objectives\n .paragraphs = Paragraphs\n .part = Part\n .problem = Problem\n .problems = Problems\n .proof = Proof\n .question = Question\n .section = Section\n .solution = Solution\n .task = Task\n .theorem = Theorem\n\n# The heading a section builds for itself: "Example 2", "Section 1.3: Limits".\n#\n# `$parts` names which pieces are present and whether a `<title>` follows, so\n# that a translation orders and punctuates each combination on its own terms\n# rather than substituting into an English frame. English puts the word first\n# and separates a title with a colon — or with a period when the heading is a\n# bare number — and none of that is a given.\n#\n# The `-title` variants end in the separator, because what follows them is the\n# title child, rendered separately. Fluent trims trailing whitespace, so the\n# separator is written as a string literal to keep its space.\n#\n# `$sectionNumber` arrives as text ("2", "1.3") rather than as a number: it is\n# an identifier made of counters, and it is not the catalog\'s to reformat.\nsection-title-prefix =\n { $parts ->\n [name] { $sectionName }\n [number] { $sectionNumber }\n [name-title] { $sectionName }{ ": " }\n [number-title] { $sectionNumber }{ ". " }\n [name-number-title] { $sectionName } { $sectionNumber }{ ": " }\n *[name-number] { $sectionName } { $sectionNumber }\n }\n\n# The heading a `<hint>` shows when the author gave it no `<title>`.\nhint-title = Hint\n\n\n## Tables and figures\n##\n## The name a `<table>` or `<figure>` gives itself, which the renderer sets in\n## bold at the head of the title or caption: "Table 2", "Figure 3". An\n## unnumbered one is named by the bare word.\n##\n## The word and the number are one message rather than a word the code\n## concatenates a number onto, so that a language which orders or punctuates\n## them differently can say so.\n##\n## `$enumeration` arrives as text rather than as a number: it identifies the\n## table, so the thousandth one is "Table 1000" and not "Table 1,000".\n##\n## The `-title` and `-caption` branches end in the separator joining the name\n## to the authored title or caption, which the renderer used to write as a\n## literal `": "` of its own. That child is arbitrary marked-up content\n## rendered after this string rather than an argument passed into it, so where\n## it sits is the one thing a translation cannot reorder — the same shape\n## `section-title-prefix` has.\n\n## The name a `<chart>` gives a series that the author did not label.\n##\n## This is not decoration: it is the text a screen reader announces on the\n## level between the chart and its marks, so a reader stopping there is told\n## which group they have reached. A bare number would be indistinguishable\n## from a value or a category, which are what the levels either side of it\n## announce.\n##\n## `$position` arrives as text rather than as a number, for the reason\n## `table-name`\'s `$enumeration` does: it identifies the series, so the\n## thousandth one is "series 1000" and not "series 1,000".\nchart-unlabeled-series =\n series { $position }\n\ntable-name =\n { $parts ->\n [numbered] Table { $enumeration }\n [numbered-title] Table { $enumeration }{ ": " }\n [unnumbered-title] Table{ ": " }\n *[unnumbered] Table\n }\n\nfigure-name =\n { $parts ->\n [numbered] Figure { $enumeration }\n [numbered-caption] Figure { $enumeration }{ ": " }\n [unnumbered-caption] Figure{ ": " }\n *[unnumbered] Figure\n }\n\n\n## Paginator controls\n##\n## The strip of controls that steps through a `<paginator>`\'s pages. All three\n## words are author-overridable attributes, so only the default is translated.\n\npaginator-previous = Previous\npaginator-next = Next\npaginator-page = Page\n\n# The status between the two buttons: "Page 3 of 5".\n#\n# Composed in the worker rather than in the renderer so that the whole of it is\n# in one language: `$pageLabel` is the `pageLabel` attribute, which an author\n# may have written themselves, and the words joining it to the counts have to\n# sit beside it in the same tongue.\n#\n# The two counts arrive as text, for the same reason `$enumeration` does above.\npaginator-page-status = { $pageLabel } { $currentPage } of { $numPages }\n\n\n## Piecewise functions\n##\n## `<piecewiseFunction>` renders each branch\'s domain as mathematics and writes\n## these three words around it: "if 1 < x < 2 or 4 < x < 5", and "otherwise"\n## for the branch that catches what the others leave. The inequalities are\n## notation and stay as they are; these are prose, and are read aloud as prose.\n##\n## `or` is the only conjunction the core composes into content. Everything else\n## that reads as "a, b, and c" is a diagnostic, and those are joined by\n## `Intl.ListFormat` at the moment the reader\'s language is known rather than\n## by a catalog word (see `DiagnosticListArg`).\n\npiecewise-condition-or = or\n\n# Introduces the domain a branch applies on; the mathematics follows it.\npiecewise-condition-if = if\n\n# The last branch, which applies wherever none of the earlier ones do.\npiecewise-condition-otherwise = otherwise\n\n\n## Chemistry\n##\n## The names the chemistry components generate. Symbols (`H`, `Fe`), formulas,\n## and anything an author\'s `<award>` compares against by value stay as they\n## are; only what is displayed as prose is here.\n##\n## Element names are keyed by symbol, which is the identifier the atom database\n## is read through and is itself never translated.\n##\n## An element\'s periodic group, its phase at STP and its metal category are not\n## here. They are enumerated values an author\'s `<award>` compares against —\n## `$atom.groupName = Noble Gas` — and translating one would break that\n## comparison the moment a document declared a language (#1577).\n\nelement-name =\n .h = Hydrogen\n .he = Helium\n .li = Lithium\n .be = Beryllium\n .b = Boron\n .c = Carbon\n .n = Nitrogen\n .o = Oxygen\n .f = Fluorine\n .ne = Neon\n .na = Sodium\n .mg = Magnesium\n .al = Aluminum\n .si = Silicon\n .p = Phosphorus\n .s = Sulfur\n .cl = Chlorine\n .ar = Argon\n .k = Potassium\n .ca = Calcium\n .sc = Scandium\n .ti = Titanium\n .v = Vanadium\n .cr = Chromium\n .mn = Manganese\n .fe = Iron\n .co = Cobalt\n .ni = Nickel\n .cu = Copper\n .zn = Zinc\n .ga = Gallium\n .ge = Germanium\n .as = Arsenic\n .se = Selenium\n .br = Bromine\n .kr = Krypton\n .rb = Rubidium\n .sr = Strontium\n .y = Yttrium\n .zr = Zirconium\n .nb = Niobium\n .mo = Molybdenum\n .tc = Technetium\n .ru = Ruthenium\n .rh = Rhodium\n .pd = Palladium\n .ag = Silver\n .cd = Cadmium\n .in = Indium\n .sn = Tin\n .sb = Antimony\n .te = Tellurium\n .i = Iodine\n .xe = Xenon\n .cs = Cesium\n .ba = Barium\n .la = Lanthanum\n .ce = Cerium\n .pr = Praseodymium\n .nd = Neodymium\n .pm = Promethium\n .sm = Samarium\n .eu = Europium\n .gd = Gadolinium\n .tb = Terbium\n .dy = Dysprosium\n .ho = Holmium\n .er = Erbium\n .tm = Thulium\n .yb = Ytterbium\n .lu = Lutetium\n .hf = Hafnium\n .ta = Tantalum\n .w = Tungsten\n .re = Rhenium\n .os = Osmium\n .ir = Iridium\n .pt = Platinum\n .au = Gold\n .hg = Mercury\n .tl = Thallium\n .pb = Lead\n .bi = Bismuth\n .po = Polonium\n .at = Astatine\n .rn = Radon\n .fr = Francium\n .ra = Radium\n .ac = Actinium\n .th = Thorium\n .pa = Protactinium\n .u = Uranium\n .np = Neptunium\n .pu = Plutonium\n .am = Americium\n .cm = Curium\n .bk = Berkelium\n .cf = Californium\n .es = Einsteinium\n .fm = Fermium\n .md = Mendelevium\n .no = Nobelium\n .lr = Lawrencium\n .rf = Rutherfordium\n .db = Dubnium\n .sg = Seaborgium\n .bh = Bohrium\n .hs = Hassium\n .mt = Meitnerium\n .ds = Darmstadtium\n .rg = Roentgenium\n .cn = Copernicium\n .nh = Nihonium\n .fl = Flerovium\n .mc = Moscovium\n .lv = Livermorium\n .ts = Tennessine\n .og = Oganesson\n\n# The name an anion takes, for the elements that have one.\n#\n# English derives these — strip a trailing "ine", add "ide", with a small table\n# for the words that rule does not fit — but that is English morphology, not a\n# fact about anions. So each language supplies the names it uses. An element\n# with no entry here is called by its own name, which is also what the English\n# rule does for everything it does not match.\nelement-anion-name =\n .h = Hydride\n .c = Carbide\n .n = Nitride\n .o = Oxide\n .f = Fluoride\n .p = Phosphide\n .s = Sulfide\n .cl = Chloride\n .br = Bromide\n .i = Iodide\n .at = Astatide\n .ts = Tennesside\n\n# A transition metal\'s ion, named with its oxidation state: "Iron (II)".\n#\n# The Roman numeral is IUPAC notation and is the same in every language. Where\n# it sits and how it is punctuated is not, which is why this is a message and\n# not a suffix the code appends.\nion-name-oxidation-state = { $name } ({ $numeral })\n\n# Shown in place of a chemical symbol or formula that names nothing. Rendered\n# into mathematics as well as into text, so the brackets around it are added by\n# the code — inside `\\text{}` for the LaTeX form — and are not part of the\n# message.\nchemistry-invalid-symbol = Invalid Chemical Symbol\nchemistry-invalid-ionic-compound = Invalid Ionic Compound\n\n## Inputs embedded in math\n\n# Names the gap an input fills when it is drawn inside a typeset expression, so\n# that a screen reader reads "x equals blank plus 3" rather than skipping over\n# it. Written into the mathematics itself and never shown on screen, so keep it\n# to a word or two.\nmath-embedded-input-blank = blank\n\n# The same, when one expression has more than one gap, so that a reader can tell\n# which one they have reached.\nmath-embedded-input-blank-ordinal = blank { $ordinal } of { $total }\n\n# The five-number summary a `<chart type="box">` gives a screen reader on each\n# box it draws.\n#\n# Words rather than five bare numbers. A bar is announced as its category and\n# its value, and a point as its coordinates, because in both the position says\n# which number it is; a box reports five numbers at one position, and nothing\n# but the naming tells them apart. A histogram\'s bar needs a word for the same\n# reason — see `chart-histogram-bin` below, which names its count.\n#\n# Each number arrives as text, already written the way the chart writes the\n# numbers on its own axis. Passed as a number instead, a translation would\n# format it a second time — grouping it, and rounding it to three fraction\n# digits — so a reader would hear a different figure from the one drawn.\nchart-box-summary =\n minimum { $minimum }, first quartile { $quartile1 }, median { $median }, third quartile { $quartile3 }, maximum { $maximum }\n\n# The name a `<chart type="box">` gives one observation drawn beyond a whisker.\n#\n# Named rather than announced as a bare number, for the reason the summary\n# above is worded: after the five numbers of the box, a further number on its\n# own says nothing about why it is drawn apart from them.\nchart-box-outlier =\n outlier { $value }\n\n# What a `<chart type="histogram">` gives a screen reader on each bar it draws:\n# the stretch of the scale the bar covers, and how many observations fell in it.\n#\n# The count is named where the two cut points are not, because the position says\n# what they are — a reader hears them in the order they are drawn in, as the ends\n# of the bar — and says nothing about the third number, which is measured up the\n# other axis.\n#\n# Each number arrives as text, already written the way the chart writes the\n# numbers on its own axis, for the reason the box plot\'s summary gives: passed\n# as a number, a translation would format it a second time and a reader would\n# hear a different figure from the one drawn.\nchart-histogram-bin =\n { $from } to { $to }, count { $count }\n';
|
|
38667
|
+
const diagnostics = '# Errors and warnings surfaced to the reader or author. Produced by the worker\n# but addressed to whoever is looking at the screen, so these are selected by\n# `uiLocale`, not `documentLocale`.\n#\n# Message ids are lower-kebab-case Fluent identifiers, optionally with a\n# single `.attribute` suffix (`invalid-attribute-value`).\n#\n# Reached by stable diagnostic code rather than by a literal `t("key")` call:\n# `DIAGNOSTIC_CODES` in `src/diagnostics.ts` maps `doenet-w0001` to the id\n# below, and `lint:i18n` treats that registry as the call site. Adding a\n# message here without registering a code for it fails the lint as an orphan.\n#\n# Translators: `through`, `endpoint`, `midpointOffset`, `numDimensions` and the\n# like are DoenetML attribute names. They are part of the language, not prose,\n# and must be left in English exactly as written.\n\n## `<lineSegment>`\n\n# $attributes is a list of attribute names; $attributesCount is its length.\nline-segment-attributes-ignored-with-endpoints =\n { $attributesCount ->\n [one] { $attributes } is ignored when two endpoints are specified\n *[other] { $attributes } are ignored when two endpoints are specified\n }\n\n# $attributes is a list of attribute names; $attributesCount is its length.\nline-segment-attributes-ignored-with-endpoint-and-midpoint =\n { $attributesCount ->\n [one] { $attributes } is ignored when an endpoint and a midpoint are both specified\n *[other] { $attributes } are ignored when an endpoint and a midpoint are both specified\n }\n\nline-segment-midpoint-offset-without-midpoint = midpointOffset has no effect without a midpoint\n\n## `<line>`\n\nline-points-undetermined-dimensions = Line through points of undetermined dimensions.\n\nline-points-too-few-dimensions = Line must be through points of at least two dimensions.\n\n# $variables is a bare enumeration of variable names, not an "and" list.\nline-points-depend-on-variables = Line is through points that depend on variables: { $variables }.\n\nline-equation-invalid-format = Invalid format for equation of line in variables { $variable1 } and { $variable2 }.\n\n## `<ray>`\n\nray-overprescribed-through = Ray is prescribed by through, endpoint, and direction. Ignoring specified through.\n\nray-dimension-mismatch = numDimensions mismatch in ray.\n\n## `<vector>`\n\nvector-overprescribed-head = Vector is prescribed by head, tail, and displacement. Ignoring specified head.\n\nvector-dimension-mismatch = numDimensions mismatch in vector.\n\n## Attracting and constraining\n\n# $component is the DoenetML tag of the child that was named, e.g. "polygon".\nattract-to-without-nearest-point = Cannot attract to a `<{ $component }>` as it doesn\'t have a nearestPoint state variable.\n\nconstrain-to-without-nearest-point = Cannot constrain to a `<{ $component }>` as it doesn\'t have a nearestPoint state variable.\n\nconstrain-to-interior-without-nearest-point = Cannot constrain to interior of a `<{ $component }>` as it doesn\'t have a nearestPoint state variable.\n\n## `<choiceInput>`\n\n# Translators: `labelPosition` is an attribute name and stays in English.\nchoice-input-label-position-ignored = labelPosition is ignored for non-inline choiceInput\n\n## Ordering children by index\n##\n## These name the component in prose rather than as a tag, matching how the\n## messages have always read. The component names stay in English; the nouns\n## around them are prose and should be translated.\n\nchoice-input-indices-count-mismatch = Ignoring indices specified for choiceInput as number of indices doesn\'t match number of choice children.\n\npretzel-indices-count-mismatch = Ignoring indices specified for problem as number of indices doesn\'t match number of problem children.\n\nshuffle-indices-count-mismatch = Ignoring indices specified for shuffle as number of indices doesn\'t match number of components.\n\n# $component is `choiceInput`, `pretzel` or `shuffle` — a DoenetML component\n# name, so it stays in English.\nindices-ignored-out-of-range = Ignoring indices specified for { $component } as some indices out of range.\n\npretzel-indices-repeated = Ignoring indices specified for pretzel as some indices are repeated.\n\npretzel-circuit-first-index = Ignoring indices specified for pretzel in circuit mode as the first index must be 1.\n\n## `<shuffle>` and `<sort>`\n\n# $component is `shuffle` or `sort`. These two components accept the same\n# children and fail the same ways, so they share their messages.\nstring-children-need-type = For `<{ $component }>` to work with string children, a `type` attribute must be specified.\n\n# $type is what the author wrote; math, text, number and boolean are attribute\n# values and stay in English.\ninvalid-type-defaulting-to-math = Invalid type { $type } for { $component } component. Must be one of math, text, number, or boolean. Defaulting to math.\n\n# Raised when `type` names something that is not one of the four readings. The\n# value is dropped rather than replaced by a guess, so the children are read as\n# though no type had been written. $type is what the author wrote.\ninvalid-type-ignored =\n Invalid type { $type } for { $component } component. Must be one of math, text, number, or boolean. Ignoring it and reading the values as though no type had been given.\n\n# $value is the string child that could not be used.\nstring-not-valid-component-to-arrange = String "{ $value }" is not a valid component to { $component }. Ignoring.\n\n## Types and variables\n\ninvalid-type-defaulting-to-number = Invalid type { $type }, setting type to number.\n\ninvalid-variable-value = Invalid value of a variable: `{ $value }`\n\n## Variants\n\n# $index is what the author wrote, reproduced verbatim rather than as a number:\n# it reached this message precisely because it was not one.\nvariant-index-must-be-number = Variant index { $index } must be a number\n\nvariant-index-must-be-integer = Variant index { $index } must be an integer\n\n## `<sideBySide>`\n\n# $component is `sideBySide` or `sbsGroup`.\nside-by-side-absolute-widths = `<{ $component }>` is not implemented for absolute measurements. Setting widths to relative.\n\nside-by-side-absolute-margins = `<{ $component }>` is not implemented for absolute measurements. Setting margins to relative.\n\nside-by-side-no-block-child = Invalid `<{ $component }>`: it must have at least one block child.\n\n## `<label>`\n\n# Translators: `for` is an attribute name and stays in English.\nlabel-for-ignored-on-graphical = The `for` attribute on graphical `<label>` is ignored.\n\nlabel-for-must-resolve-to-one = The `for` attribute on `<label>` must resolve to exactly one component.\n\nlabel-for-unresolved = The `for` attribute on `<label>` could not be resolved to a component.\n\nlabel-for-answer-with-authored-inputs = The `for` attribute on `<label>` references an `<answer>` with explicitly authored inputs; reference the input directly.\n\nlabel-for-answer-without-input = The `for` attribute on `<label>` references an `<answer>` without an input to label.\n\nlabel-for-must-reference-input-or-answer = The `for` attribute on `<label>` must reference an input or an answer.\n\n## Accessibility\n\n# $component is a DoenetML tag, e.g. "graph" or "image".\naccessibility-short-description-or-decorative = For accessibility, `<{ $component }>` must either have a short description or be specified as decorative.\n\naccessibility-video-short-description = For accessibility, `<video>` must have a short description.\n\naccessibility-input-short-description-or-label = For accessibility, `<{ $component }>` must have a short description or a label.\n\n# The companion to the message above, for the input an `<answer>` creates on the\n# author\'s behalf. Two messages rather than one with the subject passed in: the\n# subject is a phrase here, not a name, and a phrase handed over as an argument\n# would never reach a translator.\naccessibility-answer-input-short-description-or-label = For accessibility, an `<answer>` creating an input must have a short description or a label.\n\naccessibility-short-description-contains-math = Short descriptions should not contain math components such as `<{ $component }>`. Spell out any math with words.\n\n# $colorName is an attribute name and stays in English. $ratio and $threshold\n# are contrast ratios; $mode says which theme the shortfall was measured in,\n# and is `dark` or `light`.\naccessibility-section-title-insufficient-contrast =\n { $mode ->\n [dark] { $colorName } has insufficient contrast for the section heading text (dark mode) ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1).\n *[other] { $colorName } has insufficient contrast for the section heading text ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1).\n }\n\n## `<circle>`\n\n# $count is the number of through points.\ncircle-through-points-non-numerical = Haven\'t implemented `<circle>` through { $count } points in case where the points don\'t have numerical values.\n\ncircle-too-many-through-points = Cannot calculate circle through more than 3 points.\n\ncircle-overprescribed-radius-center-points = Cannot calculate circle with specified radius, center and through points.\n\ncircle-center-with-multiple-points = Cannot calculate circle with specified center through more than 1 point.\n\n# $distance and $radius arrive as strings, not numbers: $radius is the author\'s\n# own value echoed back for diagnosis, and formatting it as a quantity would\n# round a radius of 0.0001 away to 0.\ncircle-radius-too-small = Cannot calculate circle: given that the distance between the two points is { $distance }, the specified radius { $radius } is too small.\n\ncircle-radius-with-many-points = Cannot create circle through more than two points with a specified radius.\n\ncircle-invalid-center-or-through-points = Invalid center or through points of circle.\n\ncircle-radius-center-with-multiple-points = Cannot calculate radius of circle with specified center through more than 1 point.\n\ncircle-change-radius-non-numerical = Cannot change radius of circle with non-numerical through points\n\ncircle-radius-with-points-non-numerical = Cannot create circle through more than one point with specified radius when don\'t have numerical values.\n\ncircle-change-center-non-numerical = Haven\'t implemented changing center of circle through points with non numerical values.\n\n## `<function>`\n\n# Two independent counts in one sentence, so the variants multiply out. A\n# select\'s variants each need their own line, so the inner one spans lines too;\n# that is safe because newlines inside a placeable never reach the rendered\n# value. Only text continuing onto a further line would.\nfunction-domain-insufficient-dimensions =\n { $intervals ->\n [one] Insufficient dimensions for domain for function. Domain has { $intervals } interval but the function has { $inputs ->\n [one] { $inputs } input\n *[other] { $inputs } inputs\n }.\n *[other] Insufficient dimensions for domain for function. Domain has { $intervals } intervals but the function has { $inputs ->\n [one] { $inputs } input\n *[other] { $inputs } inputs\n }.\n }\n\nfunction-domain-invalid-format = Invalid format for domain for function.\n\n# $type is what was being read off the point. It selects the wording rather\n# than being substituted into it: "maximum", "slope" and the rest are English\n# nouns, and a noun handed over as an argument would never reach a translator.\n# The catch-all reproduces the pre-catalog behavior for a value not listed here.\nfunction-ignoring-non-numerical =\n { $type ->\n [maximum] Ignoring non-numerical maximum of function.\n [minimum] Ignoring non-numerical minimum of function.\n [extremum] Ignoring non-numerical extremum of function.\n [point] Ignoring non-numerical point of function.\n [slope] Ignoring non-numerical slope of function.\n *[other] Ignoring non-numerical { $type } of function.\n }\n\nfunction-ignoring-empty =\n { $type ->\n [maximum] Ignoring empty maximum of function.\n [minimum] Ignoring empty minimum of function.\n [extremum] Ignoring empty extremum of function.\n [point] Ignoring empty point of function.\n *[other] Ignoring empty { $type } of function.\n }\n\nfunction-points-too-close = Function contains two points with locations too close together. Can\'t define function.\n\nfunction-iterates-input-output-mismatch =\n { $inputs ->\n [one] Function iterates are possible only if the number of inputs of the function is equal to the number of outputs. This function has { $inputs } input and { $outputs ->\n [one] { $outputs } output\n *[other] { $outputs } outputs\n }.\n *[other] Function iterates are possible only if the number of inputs of the function is equal to the number of outputs. This function has { $inputs } inputs and { $outputs ->\n [one] { $outputs } output\n *[other] { $outputs } outputs\n }.\n }\n\n## `<sequence>`\n\nsequence-invalid-length = Invalid length of sequence. Must be a non-negative integer.\n\n# $type is a sequence type: number, letters, or math.\nsequence-invalid-step = Invalid step of sequence. Must be a number for sequence of type { $type }.\n\n# $attribute is `from` or `to` — an attribute name, so it stays in English.\nsequence-invalid-endpoint-number = Invalid "{ $attribute }" of number sequence. Must be a number.\n\nsequence-invalid-endpoint-letters = Invalid "{ $attribute }" of letters sequence. Must be a letter combination.\n\nsequence-invalid-endpoint = Invalid "{ $attribute }" of sequence.\n\nselect-from-sequence-coprime-not-numbers = coprime ignored since not selecting numbers\n\nselect-from-sequence-coprime-with-exclude-combinations = coprime ignored since excludeCombinations specified\n\n## Resolving a `target`\n##\n## Raised by the components that take a `target` attribute. They resolve it\n## through the same code and fail the same two ways, so they share these two\n## messages rather than spelling each one out per component: $source is the tag\n## of the component that raised it, part of the DoenetML language, so it stays\n## in English.\n\ntarget-not-found = Invalid target for `<{ $source }>`: cannot find target.\n\n# $property is the state variable that was looked for; $component is the tag it\n# was looked for on.\ntarget-state-variable-not-found = Invalid target for `<{ $source }>`: cannot find a state variable named "{ $property }" on a `<{ $component }>`.\n\n## `<odeSystem>`\n\node-system-variables-match-independent = Variables of `<odeSystem>` must be different than independent variable.\n\node-system-duplicate-variable-names = Can\'t define ODE RHS functions with duplicate dependent variable names.\n\node-system-rhs-function-error = Cannot define ODE RHS function. Error creating mathjs function.\n\n## `<angle>`, `<parabola>`, and `<intersection>`\n\n# $count is how many line children were found.\nangle-too-many-lines = Cannot define an angle between { $count } lines\n\nangle-invalid-through-point = Invalid point in through of `<angle>`\n\nparabola-vertex-too-many-points = Haven\'t implemented parabola with vertex through more than 1 point.\n\nparabola-too-many-points = Haven\'t implemented parabola through more than 3 points.\n\nintersection-too-many-items = Haven\'t implemented intersection for more than two items\n\n## Other math components\n\nionic-compound-not-two-ions = Have not implemented ionic compound for anything other than two ions.\n\nionic-compound-needs-cation-and-anion = Ionic compound implemented only for one cation and one anion.\n\n# $equation is the equation as the author wrote it.\nsolve-equations-cannot-evaluate = Cannot solve equation as could not evaluate equation: { $equation }\n\n# Translators: `operandNumber` is an attribute name and stays in English.\nmath-operators-operand-number-required = Must specify a operandNumber when extracting a math operand.\n\neigen-decomposition-failed = Could not calculate eigenvalues of matrix\n\n## `<matchesPattern>`\n\n# Translators: `parameters` is an attribute name and stays in English.\n# $parameters lists the parameters as the author wrote them;\n# $parametersCount is how many there were.\nmatches-pattern-parameter-not-in-pattern =\n { $parametersCount ->\n [one] `<matchesPattern>`: the parameter { $parameters } does not occur in the pattern, so it will always match a blank.\n *[other] `<matchesPattern>`: the parameters { $parameters } do not occur in the pattern, so they will always match a blank.\n }\n\n## `<graph>`\n\n# Translators: grid is an attribute name and none, medium and dense are its\n# values; all four stay in English, as does the example. $grid is the value the\n# author wrote, reproduced verbatim — it reached this message precisely because\n# it was not one of the forms listed.\ngraph-grid-invalid = `<graph>`: cannot interpret grid="{ $grid }". It must be none, medium, dense, or two positive numbers separated by a space, such as grid="1 0.5". No grid is drawn.\n\n## `<slopeField>` and `<vectorField>`\n\n# $component is the field\'s tag and $alternative the sibling field component\'s,\n# or `none` when the function would not suit it either; both are DoenetML tags\n# and stay in English, as do the example expressions. $expected is how many\n# outputs the component needs, $found how many the given function has. What a\n# field draws a mark from is the function\'s outputs, so the wrong number of\n# them means there is nothing to draw — and the author almost always wanted the\n# other component.\nfield-function-wrong-num-outputs =\n `<{ $component }>` needs a function with { $expected ->\n [one] one output, the slope y\' at each point, such as `y - x`\n *[other] two outputs, the vector at each point, such as `(y, -x)`\n }, but the function it was given has { $found ->\n [one] { $found } output\n *[other] { $found } outputs\n }. { $alternative ->\n [none] Nothing is drawn.\n *[other] `<{ $alternative }>` is the component for that function. Nothing is drawn.\n }\n\n# Translators: retired. `function` is an attribute name and stays in English.\n# It warned that a field had been given its function both as an attribute and\n# inside the component; the attribute no longer exists, so the two can no\n# longer disagree. Kept because diagnostic codes are never reused.\nfield-function-attribute-ignored-with-child = The `function` attribute is ignored because the function is also given inside the component; the one inside is used. Give the function only one of the two ways.\n\n# Translators: `variables` and `function` are a DoenetML attribute and tag and\n# stay in English, as does $component, the field\'s own tag. The attribute names\n# the inputs of an expression written directly inside the component; when there\n# is no such expression there is nothing for it to name, and $reason says which\n# of the two ways that happened.\nfield-variables-ignored =\n `<{ $component }>`: the `variables` attribute names the variables of an expression written directly inside the component. { $reason ->\n [function-child] The function here is given as a `<function>` child, which names its own variables, so `variables` is ignored.\n *[no-expression] No such expression is given here, so `variables` is ignored.\n }\n\n## PreFigure renderer\n\n# Translators: xLabelPosition, yLabelPosition and their values are attribute\n# names and stay in English, as does the renderer\'s name.\nprefigure-x-label-position-unsupported = `<graph>`: xLabelPosition="left" is not supported in prefigure renderer; using right-position behavior.\n\nprefigure-y-label-position-unsupported = `<graph>`: yLabelPosition="bottom" is not supported in prefigure renderer; using top-position behavior.\n\nprefigure-invalid-axis-bounds = `<graph>`: invalid axis bounds for prefigure conversion; using default bbox (-10,-10,10,10).\n\nprefigure-invalid-width = `<graph>`: invalid width for prefigure conversion; using default diagram width 425.\n\nprefigure-invalid-aspect-ratio = `<graph>`: invalid aspectRatio for prefigure conversion; using default aspect ratio 1.\n\n# Translators: the renderer\'s name, prefigure, stays in English. "grid" here is\n# the coordinate grid itself rather than the attribute that asks for one, so it\n# is prose and is translated.\nprefigure-grid-spacing-too-fine = `<graph>`: the grid spacing is too fine for the axis limits; the grid is omitted in the prefigure renderer.\n\nprefigure-annotations-not-rendered = `<graph>`: annotations will not be rendered when not using the PreFigure renderer.\n\nmultiple-annotations-children = Multiple `<annotations>` children found in `<graph>`; all but the last one are ignored.\n\n## Referring to other components\n##\n## `<updateValue>`\'s own "cannot find target" messages are not here: it\n## resolves a target the same way `<animateFromSequence>` does and fails the\n## same ways, so the two share `target-not-found` and\n## `target-state-variable-not-found` above.\n\ncopy-unrecognized-component-type = Cannot extend or copy an unrecognized component type: { $type }.\n\ncopy-prop-not-found = Could not find prop { $property } on a component of type { $component }\n\ncollect-no-source = No source found for collect.\n\ncollect-invalid-component-type = Cannot collect components of type `<{ $component }>` as it is an invalid component type.\n\n# $reference is the reference exactly as the author wrote it, `$` and all —\n# the `$p.styleDescription[1]` of `<text extend="$p.styleDescription[1]" />`.\n# An index only means something applied to an array, and the thing named here\n# is not one. The reference is quoted back rather than explained because the\n# text in front of the author is the only part of this they can act on: the\n# state variable and component index the core knows about are its own business\n# and go to the console instead.\nreference-index-unavailable = Cannot reference index `{ $reference }`\n\n## `<callAction>`\n\n# $action is the `actionName` the author asked for, part of the DoenetML\n# language, so it stays in English. $reference is the `target` as written.\ncomponent-action-unavailable = Cannot call { $action } on component `{ $reference }`\n\n## `<dataFrame>`\n\n# $componentIdx is an internal index, passed as a string so it is not grouped\n# like a quantity; the odd spacing before the colon is reproduced from the\n# original message.\ndata-frame-inconsistent-row-lengths = Data has invalid shape. Rows has inconsistent lengths. Found in componentIdx :{ $componentIdx }\n\ndata-frame-duplicate-column-names = Data has duplicate column names. Found in componentIdx :{ $componentIdx }\n\ndata-frame-missing-column-name = Data is missing a column name. Found in componentIdx :{ $componentIdx }\n\n## `<answer>` and scoring\n\nanswer-award-depends-on-own-response = An award for this answer is based on the answer tag\'s own submitted response, which will lead to unexpected behavior.\n\n# Translators: maxNumAttempts and sectionWideCheckWork are attribute names.\nanswer-max-num-attempts-in-section-wide-check-work = Setting `maxNumAttempts` on an `<answer>` inside a container with `sectionWideCheckWork` has no effect, as the number of attempts is controlled by the container. Set `maxNumAttempts` on the container instead.\n\nnested-section-wide-check-work-max-num-attempts = Setting `maxNumAttempts` on a container with `sectionWideCheckWork` that is inside another container with `sectionWideCheckWork` has no effect, as the number of attempts is controlled by the outer container. Set `maxNumAttempts` on the outer container instead.\n\n# $attributes is a list of attribute names; $attributesCount is its length.\nanswer-attributes-need-symbolic-equality =\n { $attributesCount ->\n [one] The { $attributes } attribute will have no effect without symbolicEquality set.\n *[other] The { $attributes } attributes will have no effect without symbolicEquality set.\n }\n\nanswer-invalid-type = Invalid type for answer: { $type }\n\n## `<module>`, `<conditionalContent>`, `<slider>`, `<pretzel>`\n\nmodule-attribute-child-needs-name = Since the component `<{ $component }>` does not have a name, it cannot be used for a module attribute\n\nmodule-attribute-name-already-defined = The component `<{ $component } name="{ $name }">` cannot be used as an attribute for a module because the `<module>` component type already has a "{ $name }" attribute defined.\n\nconditional-content-condition-ignored = Attribute `condition` is ignored on a `<conditionalContent>` component with case or else children.\n\nslider-markers-type-mismatch = Markers type doesn\'t match slider type.\n\npretzel-problem-needs-statement-and-answer = Invalid pretzel: each `<problem>` must contain one `<statement>` and one `<answer>`.\n\npretzel-circuit-first-problem-distractor = Invalid pretzel: in mode="circuit", the first `<problem>` cannot be a distractor.\n\n## Attribute values\n\n# $values is a list of the values that were rejected, each already in\n# backticks; $valuesCount is how many there were.\nattribute-invalid-values =\n { $valuesCount ->\n [one] Invalid value { $values } for attribute `{ $attribute }`; ignoring.\n *[other] Invalid values { $values } for attribute `{ $attribute }`; ignoring.\n }\n\nattribute-must-be-references = Invalid value `{ $value }` for attribute `{ $attribute }`. Attribute must be composed of references that begin with a `$`.\n\n# $names is a list of the rejected names, each already in single quotes.\nmath-input-invalid-function-names = <mathInput>: ignored invalid function name(s) in { $attribute }: { $names }. Each name\'s display segment must be at least 2 characters (letters or dashes); an optional `|<mathspeak alternative>` suffix may follow.\n\n## Building components from the source\n\n# Raised while the source is being turned into components, by throwing rather\n# than by building a record: the thrower is caught, the component becomes an\n# `_error`, and the diagnostic is re-raised from it.\n\ncomponent-type-invalid = Invalid component type: `<{ $componentType }>`\n\nattribute-repeated = Cannot repeat attribute { $attribute }.\n\nattribute-invalid-for-component = Invalid attribute "{ $attribute }" for a component of type `<{ $componentType }>`.\n\n## Style definition contrast\n\n# $context names the pair being compared, $mode which colour scheme it was\n# rendered in. Both are symbolic — the phrase is chosen here so a translator\n# can rewrite it, rather than being handed over already in English.\nstyle-definition-insufficient-contrast =\n Style definition { $styleNumber } has insufficient contrast for { $context ->\n [text-on-background] text color against background color\n [high-contrast] high-contrast color against the canvas\n [line] line color against the canvas\n [marker] marker color against the canvas\n *[text-on-canvas] text color against the canvas\n }{ $mode ->\n [dark] { " (dark mode)" }\n *[light] { "" }\n } ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1).\n\n# $suggestion says whether a concrete replacement colour could be computed.\n# The attribute names and colour values in the `available` branch are\n# DoenetML source, not prose, and stay as they are in every language.\nstyle-definition-dark-mode-text-background-contrast =\n Although style definition { $styleNumber } has specified colors that provide sufficient contrast for light mode, the dark-mode colors derived from these values have insufficient contrast for the text color against the background color ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1). { $suggestion ->\n [available] To ensure sufficient contrast in dark mode, either increase the light-mode contrast (e.g., set { $lightAttribute }="{ $lightColor }") or override the dark-mode color (e.g., set { $darkAttribute }="{ $darkColor }").\n *[none] To ensure sufficient contrast in dark mode, increase the light-mode contrast or override the derived colors with textColorDarkMode and/or backgroundColorDarkMode.\n }\n\nstyle-definition-dark-mode-text-canvas-contrast =\n Although style definition { $styleNumber } has a specified text color that provides sufficient contrast for light mode, the dark-mode text color derived from this value has insufficient contrast against the canvas ({ NUMBER($ratio, minimumFractionDigits: 2, maximumFractionDigits: 2) }:1; requires at least { $threshold }:1). { $suggestion ->\n [available] To ensure sufficient contrast in dark mode, either increase the light-mode contrast (e.g., set textColor="{ $lightColor }") or override the dark-mode color (e.g., set textColorDarkMode="{ $darkColor }").\n *[none] To ensure sufficient contrast in dark mode, increase the light-mode contrast or override the derived color with textColorDarkMode.\n }\n\nsection-multiple-style-palettes = A section can select only one <stylePalette>; using the last one.\n\n## Unique variants\n\n# Explanations of why a component\'s unique variants could not be worked out.\n# $component is the tag that could not be analyzed and stays as written; the\n# reason is a separate message per situation, so a host can tell them apart by\n# code and a translator sees a whole sentence rather than a fragment.\n\nvariant-num-to-select-not-non-negative-integer = cannot determine unique variants of { $component } as numToSelect isn\'t a non-negative integer.\n\nvariant-num-to-select-not-constant-number = cannot determine unique variants of { $component } as numToSelect isn\'t constant number.\n\nvariant-with-replacement-not-constant-boolean = cannot determine unique variants of { $component } as withReplacement isn\'t constant boolean.\n\nvariant-select-weight-disables-unique = Unique variants for select disabled if have an option with selectWeight or selectForVariants specified\n\nvariant-coprime-undetermined = cannot determine unique variants of { $component } as cannot determine coprime is always false.\n\n# $attribute is an attribute name (`from`, `to`, `step`, `sort`, `length`) and\n# stays as written.\nvariant-attribute-not-constant = cannot determine unique variants of { $component } as { $attribute } isn\'t a constant.\n\nvariant-attribute-not-number = cannot determine unique variants of { $component } as { $attribute } isn\'t a number.\n\n# $type is the sequence type the component was declared with. $expected names\n# what the value had to be, symbolically, because which one applies depends on\n# both the type and the attribute.\nvariant-attribute-wrong-type-for-sequence =\n cannot determine unique variants of { $component } of { $type } type as { $attribute } isn\'t { $expected ->\n [letters-combination] a combination of letters\n [math-expression] a valid math expression\n [integer] an integer\n *[number] a number\n }.\n\nvariant-length-not-integer = cannot determine unique variants of { $component } as length isn\'t an integer.\n\nvariant-sort-not-implemented = have not implemented unique variants of a { $component } with sort\n\nvariant-exclude-combinations-not-implemented = have not implemented unique variants of a { $component } with excludeCombinations\n\nvariant-math-exclude-not-implemented = have not implemented unique variants of a { $component } of type math with exclude\n\nvariant-non-constant-exclude-not-implemented = have not implemented unique variants of a { $component } with non-constant exclude\n\n## PreFigure conversion\n\n# $subject identifies the component the warning is about, already written as\n# `<tag>` or `<tag> (name)`. It is composed in code rather than here because\n# Fluent terms cannot take a variable as an argument, so a shared subject\n# fragment cannot be parameterized from the catalog. It holds only a tag name,\n# a component name and punctuation — never a word — which is why a descendant\n# with no type reads `<?>` rather than `<unknown>`.\n\nprefigure-descendant-unsupported = { $subject }: unsupported in graph prefigure renderer; descendant skipped.\n\nprefigure-descendant-invalid-geometry = { $subject }: non-finite or incomplete geometry; descendant skipped.\n\nprefigure-curve-label-omitted = { $subject }: labels are not supported on converted curve elements; label omitted.\n\nprefigure-curve-unsupported-definition-type = { $subject }: unsupported curve function definition type \'{ $definitionType }\'; descendant skipped.\n\nprefigure-region-flip-functions-unsupported = { $subject }: unsupported flipFunctions attribute on regionBetweenCurves; descendant skipped.\n\nprefigure-region-non-formula-child = { $subject }: only formula-typed child functions are supported on regionBetweenCurves; descendant skipped.\n\n# $labelKind says which family of object carried the label, since the advice\n# is the same but the object is not.\nprefigure-label-position-unsupported =\n { $subject }: unsupported labelPosition \'{ $labelPosition }\' for { $labelKind ->\n [line-family] line-family label\n *[point] point label\n }; default PreFigure alignment used.\n\nprefigure-fill-style-unsupported = { $subject }: fill style \'{ $fillStyle }\' is unsupported by PreFigure; falling back to a solid fill.\n\nprefigure-line-style-unknown = { $subject }: unknown line style \'{ $lineStyle }\' omitted from PreFigure output.\n\nprefigure-marker-style-mapped-to-diamond = { $subject }: marker style \'{ $markerStyle }\' mapped to PreFigure style \'diamond\'.\n\nprefigure-marker-style-unsupported = { $subject }: marker style \'{ $markerStyle }\' is unsupported by PreFigure; default style used.\n\n## PreFigure annotations\n\nannotation-ref-unresolvable = `<annotation>`: invalid `ref`; cannot resolve target. Annotation omitted.\n\nannotation-ref-multiple-targets = `<annotation>`: `ref` resolved to multiple targets; using the first target.\n\nannotation-ref-outside-graph = `<annotation>`: invalid `ref`; target is outside the containing graph. Annotation omitted.\n\nannotation-ref-unsupported-target = `<annotation>`: invalid `ref`; target is not a supported graphical object in prefigure conversion. Annotation omitted.\n\nannotation-text-missing = `<annotation>`: missing or empty `text`; emitting empty text.\n\n## Composites and references\n\n# $componentType is the type the composite was asked to create, when it is\n# known; `none` when the composite did not say.\ncomposite-circular-dependency =\n { $componentType ->\n [none] Circular dependency detected.\n *[other] Circular dependency detected involving `<{ $componentType }>` component.\n }\n\n# $reference is the reference as the author wrote it, already carrying its `$`.\nreference-no-referent = No referent found for reference: `{ $reference }`\n\nreference-multiple-referents = Multiple referents found for reference: `{ $reference }`\n\n## Children that do not match\n\nchildren-invalid-attribute-format = Invalid format for attribute { $attribute } of `<{ $componentType }>`.\n\n# $children is the list of child types that did not match, already joined.\nchildren-invalid = Invalid children for `<{ $componentType }>`: Found invalid children: { $children }\n\n## Falling back to a default\n\n# The attribute had a default to fall back to, and the message names it: the\n# component went on to behave as though the author had written that value, and\n# an author not told which one goes looking for the difference in the wrong\n# place.\nattribute-value-invalid-using-default = Invalid value `{ $value }` for attribute `{ $attribute }`, using value `{ $default }`\n\n# The same rejection where the attribute has no default to fall back to, so\n# nothing stood in for the value and the component behaves as though the\n# attribute had not been written. A separate message rather than the one above\n# with a blank in it: "using value `null`" would describe a substitution that\n# did not happen, and name a value no author could have typed. What follows is\n# usually a second message from the component itself, saying what it did\n# without the attribute.\nattribute-value-invalid-ignoring = Invalid value `{ $value }` for attribute `{ $attribute }`, ignoring it\n\n## Loading a DoenetML version\n\n# $fallback is the version that will be used instead, or `none` when the\n# embedding page named a standalone bundle of its own.\ndoenetml-version-not-found =\n { $fallback ->\n [none] DoenetML version { $version } not found.\n *[other] DoenetML version { $version } not found. Falling back to version { $fallback }\n }\n\n## Reading the DoenetML\n\n# The parser\'s own diagnostics: what the author sees before anything runs, and\n# for a beginner usually the first Doenet message they ever read.\n#\n# The parser writes its English beside the code rather than rendering it from\n# here, because `@doenet/parser` is inside the language-server bundle and a\n# catalog there is dead weight on the editor\'s critical path\n# (`packages/lsp/scripts/check-server-bundle.mjs` fails if one arrives). The\n# two copies are held together by a test in that package, which parses a\n# corpus and asserts every coded error renders to exactly what the parser\n# wrote — so a message edited here without its counterpart fails there.\n#\n# $tag, $value, $attribute and their relatives quote the author\'s own source\n# back at them and stay exactly as written. The `Invalid DoenetML: ` opening is\n# repeated in each message instead of being a shared term: a term a locale\n# forgets to define renders as its own name, and a prefix on fifteen messages\n# is not worth that failure mode.\n\nparse-invalid-doenetml = Invalid DoenetML: { $content }\n\nparse-tag-missing-close-tag = Invalid DoenetML: The tag `{ $tag }` has no closing tag. Expected a self-closing tag or a `</{ $tagName }>` tag.\n\nparse-tag-error = Invalid DoenetML: Error in tag `<{ $tagName }>`\n\nparse-attribute-missing-value = Invalid DoenetML: Invalid attribute `{ $attribute }` appears to be missing a value.\n\nparse-attribute-invalid = Invalid DoenetML: Invalid attribute `{ $attribute }`\n\nparse-attribute-value-invalid = Invalid DoenetML: Invalid attribute value `{ $value }`\n\n# $quote is the quote character that would balance the pair: `"` or `\'`.\nparse-attribute-value-quote-mismatch = Invalid DoenetML: Invalid attribute value `{ $value }`. The quote marks do not match. You appear to be missing a `{ $quote }`\n\nparse-open-tag-name-missing = Invalid DoenetML: Found a tag without a tag name, e.g. `<`\n\nparse-tag-not-closed = Invalid DoenetML: Tag `{ $tag }` was not closed (a `>` appears to be missing).\n\nparse-self-closing-tag-name-missing = Invalid DoenetML: Found a tag without a tag name `<{ $content }>`\n\nparse-self-closing-tag-not-closed = Invalid DoenetML: Tag `{ $tag }` was not closed (`/>` appears to be missing).\n\nparse-tag-invalid-attributes = Invalid DoenetML: Tag `{ $tag }` is not valid. It may have incorrect attributes.\n\nparse-close-tag-name-missing = Invalid DoenetML: Found a closing tag without a tag name, e.g. `</`\n\n# $attribute is the attribute name and $value the unquoted token that followed\n# it, shown reassembled the way the author should have written it.\nparse-attribute-value-unquoted = Attribute values must be enclosed in quotes: `{ $attribute }="{ $value }"`\n\nparse-close-tag-without-open-tag = Invalid DoenetML: Found closing tag `{ $tag }`, but no corresponding opening tag\n\nparse-close-tag-mismatched = Invalid DoenetML: Mismatched closing tag. Expected `</{ $expected }>`. Found `{ $found }`\n\n# The conversion\'s fall-through: the syntax tree held a node shape it has no\n# case for. Reaching an author means the grammar and the conversion have gone\n# out of step, which is a bug in Doenet rather than in their document — hence\n# `parser-` rather than the `parse-` of every message above, which is about\n# what the author wrote. They are still the one looking at it, so it is\n# translated like any other error. $node is the node\'s own name and stays as\n# it is.\nparser-node-unconvertible = Could not convert node { $node } to Dast node.\n\n## Names\n\n# $reason says which rule the name broke, as a key rather than a phrase, so the\n# whole sentence is translatable rather than assembled from two halves.\nname-attribute-invalid =\n Invalid attribute name=\'{ $name }\'. { $reason ->\n [characters] Names can contain only letters, numbers, underscores or hyphens.\n *[start] Names must start with a letter.\n }\n\ncomponent-name-invalid-start = Invalid component name "{ $name }". Names must start with a letter.\n\n## `<answer>` sugar\n\nanswer-video-watched-missing-video = Answer with type videoWatched must have a video attribute\n\nanswer-video-watched-video-not-reference = Answer with type videoWatched must have video attribute that is a reference\n\nanswer-name-not-single-text = Answer name attribute must have a single text child\n\n## Referencing another document\n\n# $attribute is the attribute the URI was written in (`copyFrom`, `extend`, …)\n# and stays as written; $uri is the author\'s own value.\n\nexternal-doenetml-recursion-limit = Unable to retrieve external DoenetML due to too many levels of recursion. Is there a circular reference?\n\nexternal-doenetml-unavailable = Unable to retrieve DoenetML from { $attribute }="{ $uri }"\n\nexternal-doenetml-type-mismatch = Invalid DoenetML retrieved from { $attribute }="{ $uri }": it did not match the component type "{ $componentType }"\n\n## Deprecated syntax\n\n# $from and $to are attribute names and $component a tag name; all three stay\n# as written. $component is `none` for a rename that applies to every component\n# accepting the attribute, where naming one would be wrong.\n#\n# The `[deprecation]` opening is a literal marker shared by all four messages,\n# not a word: leave it as it is.\n\ndeprecated-attribute-renamed =\n { $component ->\n [none] [deprecation] Attribute `{ $from }` is deprecated; use `{ $to }` instead.\n *[other] [deprecation] Attribute `{ $from }` on `<{ $component }>` is deprecated; use `{ $to }` instead.\n }\n\ndeprecated-attribute-renamed-conflict =\n { $component ->\n [none] [deprecation] Attribute `{ $from }` is deprecated and ignored because `{ $to }` is also specified.\n *[other] [deprecation] Attribute `{ $from }` on `<{ $component }>` is deprecated and ignored because `{ $to }` is also specified.\n }\n\ndeprecated-attribute-ignored = [deprecation] Attribute `{ $attribute }` on `<{ $component }>` is deprecated and ignored.\n\n# An attribute replaced by a child element rather than by another attribute:\n# $attribute and $component stay as written, and so does $child, which is the\n# tag name of the child to write instead.\n\ndeprecated-attribute-to-child = [deprecation] Attribute `{ $attribute }` on `<{ $component }>` is deprecated; use a `<{ $child }>` child instead.\n\n# One value of an attribute is deprecated while the attribute itself stays:\n# $value is what the author wrote and $to what to write instead, with\n# $attribute and $component naming where. All four stay as written.\n\ndeprecated-attribute-value-renamed = [deprecation] Value `{ $value }` of attribute `{ $attribute }` on `<{ $component }>` is deprecated; use `{ $to }` instead.\n\n\n## Language coverage\n\n# `<pluralize>` runs an English part-of-speech model over its text and puts the\n# nouns in the plural. There is no equivalent for an arbitrary language — a\n# correct plural needs that language\'s own morphology, and often its\n# dictionary — so in a document written in anything else the word is left as\n# the author typed it, and this says so rather than silently doing nothing.\n#\n# Raised only when there is nothing else to fall back on: a `pluralForm` is the\n# author supplying their own language\'s plural, and that is honored in every\n# language, which is why this can recommend it.\n#\n# $locale is the document\'s language tag, as declared.\npluralize-english-only = `<pluralize>` can only pluralize English, so its text is left unchanged in a document written in { $locale }. Write the plural form directly, or set it with the `pluralForm` attribute.\n\n\n## Checking against the schema\n\n# What the editor draws a squiggle under: the language server\'s own check of\n# the document against the DoenetML schema, run on every keystroke and\n# answered before anything is evaluated. A beginner meets these first, and\n# usually only these, because a document that does not pass them rarely gets\n# as far as producing a diagnostic from the core.\n#\n# `@doenet/lsp-tools` writes its English beside the code rather than rendering\n# it from here, for the same reason `@doenet/parser` does: it is inside the\n# language-server bundle, which `@doenet/codemirror` embeds verbatim and\n# starts as a blob worker, and a catalog on the editor\'s critical path is what\n# `packages/lsp/scripts/check-server-bundle.mjs` exists to reject. The two\n# copies are held together by a test in that package, which runs the checker\n# over a corpus and asserts every coded violation renders to exactly what the\n# checker wrote — so a message edited here without its counterpart fails\n# there.\n#\n# $tag, $parent and $attribute quote the author\'s own source back at them and\n# stay exactly as written; the angle brackets and backticks around them are\n# punctuation this catalog supplies, not part of the name.\n\nschema-element-unrecognized = Element `<{ $tag }>` is not a recognized Doenet element.\n\nschema-element-not-allowed-at-root = Element `<{ $tag }>` is not allowed at the root of the document.\n\nschema-element-not-allowed-inside = Element `<{ $tag }>` is not allowed inside of `<{ $parent }>`.\n\nschema-attribute-unrecognized = Element `<{ $tag }>` doesn\'t have an attribute called `{ $attribute }`.\n\n# $allowed is the attribute\'s permitted values, each already in double quotes\n# and joined for the reader\'s language. $isList says whether the attribute\n# takes several of them at once: one situation, two sentences, because the\n# reader has to be told they are choosing a whole list rather than a value.\nschema-attribute-value-not-allowed =\n { $isList ->\n [true] Attribute `{ $attribute }` of element `<{ $tag }>` must be a list whose items are each one of: { $allowed }\n *[other] Attribute `{ $attribute }` of element `<{ $tag }>` must be one of: { $allowed }\n }\n\n\n## The `<select>` family\'s error boxes\n##\n## The messages that replace the whole component with a red box rather than\n## warning beside it: nothing can be selected, so there is nothing to render.\n## They were the last uncoded `_error` path in the worker (#1581).\n##\n## Counts arrive as numbers rather than as text, so a language that agrees a\n## noun with them can select on them. English does not agree here — it says\n## "1 options" today and these messages reproduce it exactly, because the box\'s\n## text is what the existing suites pin.\n##\n## Being numbers, they are grouped: a count of 1500 renders as "1,500" in\n## English where the concatenated sentence these replaced wrote "1500". That is\n## the one way the English moved, and it moved in the right direction — these\n## are quantities, not identifiers like a line or a section number, which is\n## the distinction that decides between a number and a string everywhere in\n## these catalogs.\n##\n## Translators: component and attribute names — `selectFromSequence`,\n## `selectPrimeNumbers`, `from`, `to`, `step` — are DoenetML identifiers, not\n## words. They are written into these messages as they stand and must be left\n## in English exactly as written.\n\nselect-variant-name-option-count-mismatch = Invalid variant name for select. Variant name { $variantName } appears in { $numOptions } options but number to select is { $numToSelect }.\n\nselect-variant-name-without-options = Some variants are specified for select but no options are specified for possible variant name: { $variantName }.\n\nselect-variant-name-not-possible = Variant name { $variantName } that is specified for select is not a possible variant name.\n\nselect-too-few-options = Cannot select { $numToSelect } components from only { $numOptions }.\n\nselect-from-sequence-too-few-values = Cannot select { $numToSelect } values from a sequence of length { $length }.\n\nselect-from-sequence-indices-count-mismatch = Number of indices specified for select must match number to select\n\nselect-from-sequence-indices-not-integers = All indices specified for select must be integers\n\nselect-from-sequence-index-excluded = Specified index of selectfromsequence that was excluded\n\nselect-from-sequence-indices-excluded-combination = Specified indices of selectfromsequence that was an excluded combination\n\nselect-from-sequence-coprime-not-positive-integers = Cannot select coprime combinations as not selecting positive integers.\n\n# Translators: from, to and step are attribute names. They are written into\n# the message rather than passed in because these three never vary — an\n# argument is for a name that changes from one call to the next.\nselect-from-sequence-coprime-common-factor = Cannot select coprime numbers. All possible values share a common factor. (Specified values of "from" or "to" must be coprime with "step".)\n\nselect-from-sequence-coprime-single-number = Cannot select coprime combinations from a single number that is not 1.\n\nselect-from-sequence-excluded-too-many-combinations = Excluded over 70% of combinations in selectFromSequence\n\n# The sibling of `select-from-sequence-coprime-common-factor`, and a different\n# situation rather than a rewording of it: that one is decided up front, from\n# the sequence\'s own arithmetic. This one is what is left after two hundred\n# draws found no coprime combination among values that could have supplied one.\nselect-from-sequence-coprime-none-found = Could not select coprime numbers. All possible values share a common factor.\n\nselect-from-sequence-too-few-unique-values = Cannot select { $numToSelect } unique values from sequence of length { $numPossibleValues }\n\nselect-prime-numbers-too-few-values = Cannot select { $numToSelect } values from a list of primes of length { $numValues }\n\nselect-prime-numbers-values-count-mismatch = Number of values specified for select must match number to select\n\nselect-prime-numbers-values-not-prime = All values specified for select prime number must be in the list of primes\n\nselect-prime-numbers-values-excluded-combination = Specified values of selectPrimeNumbers was an excluded combination\n\nselect-prime-numbers-excluded-too-many-combinations = Excluded over 70% of combinations in selectPrimeNumbers\n\n# Both flukes are shared by `<selectFromSequence>` and `<selectPrimeNumbers>`,\n# which say the same thing in the same words. Neither is reachable in practice\n# — each follows two hundred independent draws — but an unreachable box is\n# still a box, and it renders in whatever language the rest of the page does.\nselect-random-combination-fluke = By extremely unlikely fluke, couldn\'t select combination of random values\n\nselect-random-value-fluke = By extremely unlikely fluke, couldn\'t select random value\n\n## Inputs embedded in math\n\n# Translators: $component is the input\'s own DoenetML tag and stays in English,\n# as do the attribute names `inline`, `expanded` and `width`. An input written\n# inside `<m>` is drawn in the place it is written, inside the typeset\n# expression, which makes room for it as it grows. That needs a control small\n# enough to sit in a line of mathematics, with a width the expression can\n# measure, in an expression that is not drawn on a graph; $reason says which of\n# those this input fails. When it fails, the input is not shown at all, and the\n# expression is typeset with the input\'s value in its place — a text input\'s\n# text, a math input\'s mathematics — or with nothing there for a choice input.\nmath-embedded-input-shape-unsuitable =\n { $reason ->\n [not-inline] This `<{ $component }>` is not shown because it is inside math and is not `inline`. Add `inline` so it becomes a drop-down list, which fits inside an expression.\n [expanded] This `<{ $component }>` is not shown because it is inside math and is `expanded`. Remove `expanded`; a multi-line box does not fit inside an expression.\n [on-graph] This `<{ $component }>` is not shown because it is inside math drawn on a graph, which has no room for an input.\n *[relative-width] This `<{ $component }>` is not shown because it is inside math and has a relative width. Give the width in absolute units, such as `px`, instead.\n }\n\n## `<sampleRandomNumbers>` and `<selectRandomNumbers>`\n##\n## Translators: `mean`, `standardDeviation`, `variance`, `numTotal`,\n## `numSuccesses`, `numDraws`, `numTrials`, `probability` and `numSamples` are\n## DoenetML attribute names. They are written into these messages as they stand\n## and must be left in English exactly as written. Each of these names the\n## attribute the author has to change, so a message that translated one would\n## point at an attribute that does not exist.\n\n# The spread reaches the sampler as a standard deviation however it was written,\n# so a negative `variance` arrives here as a `standardDeviation` of NaN and the\n# message has to name both for the author to know what to edit.\nsample-gaussian-parameters-invalid =\n Invalid mean ({ $mean }) or standardDeviation ({ $standardDeviation }) for a gaussian random variable. The mean must be a finite number, and the standardDeviation (or the variance it is derived from) must be finite and non-negative. No numbers can be sampled.\n\n# These three attributes are the only ones with no default, so leaving one out is\n# the commonest way to reach this message. Each arrives either as the number the\n# author wrote or as `not-set` for an attribute they left off entirely; translate\n# the "not set" wording, but leave the `not-set` key that selects it untouched.\nsample-hypergeometric-parameters-invalid =\n Invalid numTotal ({ $numTotal ->\n [not-set] not set\n *[other] { $numTotal }\n }), numSuccesses ({ $numSuccesses ->\n [not-set] not set\n *[other] { $numSuccesses }\n }), or numDraws ({ $numDraws ->\n [not-set] not set\n *[other] { $numDraws }\n }) for a hypergeometric random variable. numTotal must be a positive whole number, and numSuccesses and numDraws must be non-negative whole numbers no larger than numTotal. All three must also stay below about nine quadrillion, past which whole numbers can no longer be counted exactly.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\nsample-hypergeometric-draws-too-many =\n Drawing { $numDraws } items from a population of { $numTotal } would need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce numDraws, or bring it closer to numTotal.\n\nsample-binomial-parameters-invalid =\n Invalid numTrials ({ $numTrials }) or probability ({ $probability }) for a binomial random variable. numTrials must be a non-negative whole number below about nine quadrillion, past which whole numbers can no longer be counted exactly, and probability must be between 0 and 1.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\nsample-binomial-trials-too-many =\n Running { $numTrials } trials would need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce numTrials.\n\nsample-poisson-mean-invalid =\n Invalid mean ({ $mean }) for a poisson random variable. The mean must be a finite, non-negative number.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\nsample-poisson-mean-too-large =\n A poisson mean of { $mean } would need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce the mean.\n\n# $distribution names the distribution being sampled, e.g. "binomial" or\n# "multivariate hypergeometric"; $draws is roughly how many random draws each value\n# needs. Raised by `<sampleRandomNumbers>`, `<selectRandomNumbers>` and\n# `<sampleMultivariateRandomNumber>`, which count values with `numSamples`,\n# `numToSelect` and one vector respectively, so the wording names no attribute.\nsample-distribution-slow =\n Each value from this { $distribution } distribution needs about { $draws } random draws, so sampling may be slow. Reduce the distribution\'s parameters, or ask for fewer values, if the page feels sluggish.\n\n## List index operators\n\n# Raised by the index-returning list operators (`<indexOf>`, `<searchSorted>`)\n# when no `target` was given. $component names the tag the author wrote.\nindex-operator-missing-target =\n `{ $component }` has no `target` to look for, so it cannot report a position and gives 0.\n\n# Raised when the operator had no values to look through — an empty list, or one\n# whose only children were references that produced nothing.\nindex-operator-no-values =\n `{ $component }` has no values to look through, so it gives 0, which is not the index of any item.\n## `<sampleMultivariateRandomNumber>`\n##\n## Translators: `type`, `numInCategories` and `numDraws` are DoenetML attribute\n## names, and `hypergeometric` is one of the values `type` takes. They are written\n## into these messages as they stand and must be left in English exactly as\n## written. Each names the attribute the author has to change, so a message that\n## translated one would point at an attribute that does not exist.\n\n# Neither attribute has a default that names a population — numDraws has none at\n# all, and numInCategories falls back to a list with no categories in it — so\n# leaving one out is the commonest way to reach this message. Each arrives either\n# as the value the author wrote or as `not-set`: for numDraws when it is missing,\n# and for numInCategories when it names no categories, whether it was left off or\n# written empty. Translate the "not set" wording, but leave the `not-set` key that\n# selects it untouched.\nsample-multivariate-parameters-invalid =\n Invalid numInCategories ({ $numInCategories ->\n [not-set] not set\n *[other] { $numInCategories }\n }) or numDraws ({ $numDraws ->\n [not-set] not set\n *[other] { $numDraws }\n }) for a multivariate hypergeometric random variable. numInCategories must list at least one category, each a non-negative whole number, and numDraws must be a non-negative whole number no larger than their total. Each category, that total, and numDraws must also stay below about nine quadrillion, past which whole numbers can no longer be counted exactly.\n\n# $maxDraws is the largest number of random draws allowed for a single sample.\n# Drawing nearly the whole population is as cheap as drawing almost none of it, so\n# raising numDraws is a fix as well as lowering it. "could need" rather than "would\n# need": each category is counted at the most its draw could cost, so the count\n# these parameters are refused on is an upper bound on the work they really take.\nsample-multivariate-draws-too-many =\n Drawing { $numDraws } items from a population of { $numTotal } split into { $numCategories } categories could need more than { $maxDraws } random draws for each sample, which would stop the page from responding. Reduce numDraws, bring it closer to numTotal, or use fewer categories.\n\n# `type` has no default, so this is what leaving it off gets. A type the attribute\n# does not recognize falls back to no type at all and reaches this message too,\n# after a separate one naming the value that was rejected — hence "no distribution\n# was named" rather than wording that assumes the attribute is missing.\nsample-multivariate-type-not-specified =\n No multivariate distribution was named for this random variable, so nothing was sampled. Give the type attribute the name of a distribution, such as `type="hypergeometric"`.\n\n## Counting operators\n\n# Raised by `<tally>` when the author named `categories` explicitly and some\n# values matched none of them, so those values were not counted anywhere. Info\n# rather than a warning: a list fed by an input legitimately holds\n# non-categories while a student is typing. It deliberately does not say how\n# many such values there were: the diagnostics queue is append-only and\n# deduplicates by message, so a count would leave one permanent entry behind\n# for every number of stray values the list passed through while being typed.\ntally-values-outside-categories =\n Some values matched none of the declared categories, so they were not counted.\n\n# Raised by `<chart type="histogram">` when an observation falls outside the\n# outermost cut points. Only cut points an author wrote can leave any — bins the\n# chart chooses cover the data — so this says what a written `bins` left out.\n# Info rather than a warning, for the reason the message above is: leaving part\n# of a sample out of a picture is something an author may have meant, and a\n# column fed by an input passes through values outside the bins while a student\n# is typing. It deliberately does not say how many: the diagnostics queue is\n# append-only and deduplicates by message, so a count would leave one permanent\n# entry behind for every number the column passed through.\nchart-histogram-values-outside-bins =\n Some observations fell outside the `bins` cut points, so they were not counted in any bar.\n\n# Raised by `<tally>` when `categories` names the same category more than once.\n# A warning rather than info: unlike a stray value, a repeated category is a\n# property of what the author wrote, so it stays true of a settled document. The\n# message says what the counts will be, since the behavior is defensible but not\n# what an author who repeated a category by accident expects.\ntally-repeated-category =\n `categories` names the same category more than once. Each count reports the values equal to its own category, so the repeated ones all report the same number.\n\n# Raised by `<binCounts>` when no `bins` attribute was given, so there are no\n# intervals to count into. $component names the tag the author wrote.\nbin-counts-missing-bins =\n `{ $component }` has no `bins` cut points, so there are no intervals to count into and it gives no counts.\n\n# Raised by `<binCounts>` when `bins` held fewer than the two cut points needed\n# to define a single interval. $count is how many were given, and can be 0 or 1,\n# so the sentence is worded to read for both without forking on the number.\nbin-counts-too-few-cut-points =\n `bins` needs at least 2 cut points to define an interval, but received { $count }.\n\n# Raised by `<binCounts>` when its values are not all numeric, so they cannot be\n# placed between numeric cut points. $component names the tag the author wrote.\nbin-counts-values-not-numeric =\n `{ $component }` can only count numeric values into bins, but not every value it was given is numeric. All counts are 0.\n\n# Raised by `<binCounts>` when the `bins` cut points do not climb: one is\n# smaller than the one before it, so a bin would run backwards and its count\n# would come out negative, or one is not a number at all, which has the same\n# effect. Equal adjacent cut points are accepted, so the message says "at least\n# as large as" rather than "increasing": what is rejected is a decrease, not a\n# failure to strictly increase. Between two interior cut points that names an\n# empty bin; at either end the outermost-edge rule can still put a value in it.\nbin-counts-cut-points-decreasing =\n Each `bins` cut point must be a number at least as large as the one before it, but one is not a number or is smaller than its predecessor, so no counts were produced.\n\n## Charts\n\n# Raised by `<chart type="bar">` when a value is not a finite number, so it gets\n# no bar. The alternative reading of a missing bar is a value of zero, which the\n# author cannot distinguish by looking, so the absence is stated rather than left\n# to be inferred. No count: the queue is append-only and deduplicates by message,\n# and the number of such values changes as an input is typed into. (The message\n# key is `bar-chart-…` because a code names one situation forever and this one\n# was issued while the component was still spelled `<barChart>`.)\nbar-chart-values-not-drawable =\n A bar chart draws no bar for a value that is not a finite number, so those places on the chart are empty rather than zero.\n\n# Raised by `<chart type="bar">` when `barWidth` is outside the (0, 1] range a\n# fraction of a bar\'s slot can take. $barWidth is what the author wrote.\nbar-chart-bar-width-invalid =\n `barWidth` must be greater than 0 and at most 1, but { $barWidth } was given. Using 0.8 instead.\n\n# `type` has no default, so this is what `<chart>` on its own gets. A type the\n# attribute does not recognize falls back to no type at all and reaches this\n# message too, after a separate one naming the value that was rejected — hence\n# "no chart type was named" rather than wording that assumes the attribute is\n# missing.\nchart-type-not-specified =\n No chart type was named, so nothing was drawn. Give the type attribute the name of a chart, such as `type="bar"`.\n\n# Raised by `<chart>` when values are written beside its `<series>` children.\n# A value outside every series belongs to no group of the data, so there is no\n# place on the chart to draw it; saying so matters because the alternative\n# reading of the missing bar is that the value was zero.\nchart-values-outside-series =\n A chart with `<series>` children draws only the values inside them, so values written beside a series were not drawn. Move them into a series of their own.\n\n# Raised by `<chart type="line">` and `<chart type="scatter">` when a point\n# cannot be placed. Two situations reach it and the message covers both: a value\n# that is not a finite number, and a value with no `x` beside it — which is what\n# a series given fewer horizontal coordinates than values produces from the\n# position they run out. Neither is distinguishable by looking from having asked\n# for fewer points. No count: the queue deduplicates by message, and the number\n# changes as an input is typed into.\nchart-points-not-drawable =\n A point needs a finite number for both coordinates, so values with no coordinate beside them, and coordinates with no value, were not drawn.\n\n# Raised by `<chart type="pie">` for a value that is not a finite number. The\n# bar chart\'s message says the place on the chart is empty rather than zero; a\n# pie has no places, so this says the slice is missing instead. No count, for\n# the reason the others give: the queue deduplicates by message.\nchart-pie-values-not-drawable =\n A pie chart draws no slice for a value that is not a finite number, and leaves it out of the total.\n\n# Raised by `<chart type="pie">` for a negative value. Its own message rather\n# than the one above, because the reason is different in kind: a negative bar\n# hangs below the baseline it is measured from, and a pie has no baseline to\n# hang anything from, so a negative value is not merely undrawable but has no\n# reading as a share of a total. It is left out of the total as well, so the\n# slices that are drawn are shares of what was actually charted.\nchart-pie-negative-values =\n A slice is a share of a total, so a pie chart draws no slice for a negative value and leaves it out of the total.\n\n# Raised by `<chart type="pie">` when the values that could be shares of a\n# total came to zero. Nothing is drawn: there is no share to take of zero, and\n# dividing by it would put `NaN` in every angle. Not raised for a pie with no\n# values at all, which is one being written rather than one that is wrong, nor\n# for one whose every value was rejected — those already have a message naming\n# the reason.\nchart-pie-total-not-positive =\n The values of this pie chart total zero, so there are no shares to draw. A pie needs at least one value above zero.\n\n# Raised by `<chart type="pie">` holding more than one `<series>` that is not\n# hidden — a hidden one is dropped before the count, like any drawn child of a\n# container that is hidden on its own account. Concentric\n# rings are not a standard chart and a donut is a styling variant rather than a\n# second group, so there is nowhere on a pie for a second series to go.\nchart-pie-one-series =\n A pie chart draws one series, so only the first was drawn. Chart the others separately, or use a type that draws several series at once.\n\n# Raised by `<chart type="pie">` whose `<xLabel>` or `<yLabel>` child has text\n# in it; a blank one asks for nothing and is not reported. A pie\n# has no axes for either to name, so the text is authored prose with nowhere on\n# the page to go — which is worth a message where an ignored *attribute* is not,\n# because what was dropped is something the author wrote for a reader to see.\nchart-pie-axis-name-ignored =\n A pie chart has no axes, so an `<xLabel>` or `<yLabel>` was not drawn. Put the text in a `<title>` instead, or in prose beside the chart.\n\n# Raised by `<chart type="box">` for an observation that is not a finite\n# number. Left out of the summary rather than read as zero, which would move\n# every quartile of the box drawn from it. No count, for the reason the others\n# give: the queue deduplicates by message, and the number changes as an input is\n# typed into.\nchart-box-values-not-drawable =\n A box plot summarizes finite numbers, so an observation that is not one was left out of its summary.\n\n# Raised by `<chart type="box">` carrying a `categories` attribute. A box chart\n# has no categories: each of its series is one position on the axis and is named\n# by its own `<label>`, so the names in `categories` are authored text with\n# nowhere on the page to go — which is worth a message where an ignored\n# *attribute* is not, because what was dropped is something the author wrote for\n# a reader to see.\nchart-box-categories-ignored =\n A box plot names each of its boxes after the series it was drawn from, so `categories` was not used. Give each `<series>` a `<label>` instead.\n\n# Raised by `<chart type="histogram">` for an observation that is not a finite\n# number, which falls in no bin. Its own message rather than the box plot\'s,\n# because what is lost differs: there a quartile moves, here a bar is one\n# shorter. No count, for the reason the others give: the queue deduplicates by\n# message, and the number changes as an input is typed into.\nchart-histogram-values-not-drawable =\n A histogram counts finite numbers, so an observation that is not one fell in no bin.\n\n# Raised by `<chart type="histogram">` holding more than one `<series>` that is\n# not hidden — a hidden one is dropped before the count, like any drawn child of\n# a container that is hidden on its own account. Two samples counted into the\n# same bars would have to be stacked or drawn through each other, and neither is\n# a reading a histogram can be given without being told which was meant.\nchart-histogram-one-series =\n A histogram draws one series, so only the first was drawn. Chart the others separately, or compare them with `type="box"`.\n\n# Raised by `<chart type="histogram">` whose `bins` is a single number that is\n# not a number of bins: not a whole number, not positive, or more bins than a\n# picture can hold. $bins is what the author wrote and $maximum the most that\n# may be asked for. Two or more numbers are read as cut points instead, and are\n# reported by the message below.\nchart-histogram-bin-count-invalid =\n `bins` must be a whole number of bins from 1 to { $maximum }, or two or more cut points, but { $bins } was given. Choosing bins from the data instead.\n\n# Raised by `<chart type="histogram">` whose `bins` holds two or more numbers\n# that do not describe bins to draw. `<binCounts>` has a message of its own for\n# the same list, and this one differs in what happens next: a chart has data to\n# choose bins from, so it draws those rather than drawing nothing.\n#\n# A cut point that is not finite is reported here too, where `<binCounts>`\n# counts into a bin with no far end quite happily — a bar with no far end is not\n# something a picture can hold.\nchart-histogram-cut-points-invalid =\n Each `bins` cut point must be a finite number at least as large as the one before it. Choosing bins from the data instead.\n\n# Raised by `<chart type="histogram">` carrying a `categories` attribute. A\n# histogram has no categories: its positions are the bins it divided the scale\n# into, named by the numbers they run between, so the names in `categories` are\n# authored text with nowhere on the page to go — which is worth a message where\n# an ignored *attribute* is not, because what was dropped is something the\n# author wrote for a reader to see.\nchart-histogram-categories-ignored =\n A histogram names its bars by the cut points they run between, so `categories` was not used. Give `bins` the cut points you want instead.\n\n# Raised by `<chart type="histogram">` carrying a `barWidth` attribute. The gap\n# a `barWidth` below 1 leaves is what says two bars stand for separate things,\n# and a histogram\'s bars are neighboring stretches of one continuous scale —\n# so there is no slot for a width to be a fraction of.\nchart-histogram-bar-width-ignored =\n A histogram\'s bars are adjacent, with no gap between them, so `barWidth` was not used. Give `bins` fewer cut points to draw wider bars.\n';
|
|
38636
38668
|
const editor = '# Editor and language-server surfaces: the footer, the diagnostics panel, the\n# variant picker, the accessibility button, and the context-help panel beside\n# them. Selected by `uiLocale`.\n#\n# The editor binds to *one* language, and it is the one the viewer below it\n# resolved rather than the one the surrounding host chrome uses: the panel\n# lists diagnostics rendered down there, where an authored `<document lang>` is\n# known, and a footer in one language above a panel in another reads worse than\n# either choice on its own (#1580).\n#\n# The LSP ships bundled with its DoenetML version, so these catalogs are\n# version-correct rather than always-latest.\n#\n# Message ids are lower-kebab-case Fluent identifiers, optionally with a\n# single `.attribute` suffix (`format-document`).\n\n\n## The viewer\'s controls\n##\n## `editor-update-viewer` names the button that recompiles the document. Its\n## word depends on whether the source changed or only the document\'s state\n## did, which is a distinction the code makes and a `$action` says: the button\n## is otherwise identical, and splitting it into two messages would let the two\n## drift.\n##\n## The keyboard hint is a separate branch rather than text appended after the\n## title, because where a shortcut sits in a sentence is not the same in every\n## language. `$shortcut` is a key combination and stays as written.\n##\n## The tooltip takes the word as `$word` rather than referencing the message\n## above it: a message reference resolves only inside its own bundle, so a\n## locale that translated the tooltip but not the word would render the\n## reference literally instead of falling back to English. This is the shape\n## `paginator-page-status` already uses.\n\n# Button label. "Update" recompiles after a source edit; "Reset" reverts\n# only the document\'s live state to match — same button, different verb.\n# Keep both forms short enough to fit a toolbar button.\neditor-update-viewer =\n { $action ->\n [reset] Reset\n *[update] Update\n }\n\n# Tooltip on the button above. { $word } is that button\'s own current label\n# ("Update" or "Reset"), so this reads as e.g. "Update Viewer" or\n# "Reset Viewer". { $shortcut } appends the keyboard shortcut when one exists.\neditor-update-viewer-title =\n { $shortcut ->\n [none] { $word } Viewer\n *[other] { $word } Viewer { $shortcut }\n }\n\n\n## The variant picker\n\n# Label for the variant picker control itself (a noun, like a field label).\neditor-variant = Variant\n\n# Placeholder text shown in the empty filter input, before the user types\n# anything — not a button or instruction.\neditor-variant-filter = Filter...\n\n# Button/action label: advances to the next variant in the list.\neditor-variant-next = Select next variant\n\n# Button/action label: goes back to the previous variant in the list.\neditor-variant-previous = Select previous variant\n\n\n## The accessibility status button\n##\n## `WCAG AA` is the name of the standard and is not translated, the way\n## `accessibility-heading-level-1` already leaves it.\n##\n## The tooltip and the label are separate messages rather than one with an\n## extra clause: the label counts the violations and the tooltip does not, and\n## a screen reader and a hover are different audiences.\n##\n## `$action` is whether clicking opens or closes the report — an interpolated\n## English verb before this, which no locale could reach and which no language\n## has to place where English does.\n\neditor-accessibility-title =\n { $status ->\n [violations] WCAG AA accessibility violation identified. Click to { $action ->\n [close] close\n *[open] open\n } accessibility report.\n [advisories] Click to { $action ->\n [close] close\n *[open] open\n } accessibility report. No WCAG AA violations were found, but additional accessibility recommendations are available.\n *[clean] Click to { $action ->\n [close] close\n *[open] open\n } accessibility report. No accessibility issues were found.\n }\n\neditor-accessibility-label =\n { $status ->\n [violations] WCAG AA accessibility violation identified. { $count ->\n [one] { $count } WCAG AA violation\n *[other] { $count } WCAG AA violations\n } found. Click to { $action ->\n [close] close\n *[open] open\n } accessibility report.\n [advisories] No WCAG AA violations identified. { $count ->\n [one] { $count } additional accessibility recommendation\n *[other] { $count } additional accessibility recommendations\n } found. Click to { $action ->\n [close] close\n *[open] open\n } accessibility report.\n *[clean] No WCAG AA violations identified. Click to { $action ->\n [close] close\n *[open] open\n } accessibility report.\n }\n\n# The word on the button itself, beside the icon, when there are violations.\neditor-accessibility-badge = WCAG\n\n\n## The footer\n##\n## `$version` is a version number and stays as written.\n\neditor-version-title = DoenetML version { $version }\n\neditor-tab-help = Context-sensitive help\n# The word beside the icon on that tab, which has room for one word.\neditor-tab-help-short = Context\neditor-tab-errors = Errors\neditor-tab-warnings = Warnings\neditor-tab-info = Info\neditor-tab-accessibility = Accessibility\neditor-tab-responses = Submitted responses\n\n# A tab\'s accessible name when it carries a count: "Errors: 3".\neditor-tab-with-count = { $label }: { $count }\n\neditor-options = Editor options\neditor-format-as-doenetml = Format as DoenetML\neditor-format-as-xml = Format as XML\n\n\n## The diagnostics panel\n\n# Where in the source a diagnostic was found. `$line` arrives as text, not as a\n# number: it identifies the line, so line 1234 is "#1234" and not "#1,234".\neditor-diagnostic-line = Line #{ $line }\n\neditor-no-errors = No Errors\neditor-no-warnings = No Warnings\neditor-no-info = No Info Diagnostics\n\neditor-show-info-annotations = Show info diagnostics in editor\neditor-show-accessibility-annotations = Show accessibility diagnostics in editor\n\n# Links into the documentation, which has no translated pages yet. The words\n# move; where they point does not (#1580).\neditor-accessibility-learn-more = Learn how Doenet approaches accessibility\n\n# The heading over the WCAG AA failures. The standard\'s name is a link, so it\n# arrives as an argument rather than being written into the sentence — the\n# brackets around it are this catalog\'s punctuation, and the link is the code\'s.\neditor-accessibility-violations-heading = Accessibility violations ({ $standard })\n\neditor-accessibility-other-heading = Other accessibility issues\neditor-none-found = None found\n\n\n## Submitted responses\n\neditor-no-responses = No submitted responses yet\neditor-response-answer-id = Answer Id\neditor-response-response = Response\neditor-response-credit = Credit\neditor-response-submitted = Submitted\n\n\n## The context-help panel\n##\n## The panel beside the diagnostics, which explains whatever the cursor is on.\n## It is the last of the surfaces the header above names.\n##\n## Several of these sentences have marked-up fragments in them — an element\n## name in `<code>`, a link, a rendered piece of inline markdown. Those arrive\n## as arguments and are put back as React nodes after the message is formatted,\n## so a translation owns the whole sentence including where each fragment sits\n## and the punctuation around it. A translation that drops one simply renders\n## without it rather than losing the sentence.\n##\n## Element names, attribute names and `styleNumber` are DoenetML identifiers\n## and stay as written. The descriptions and summaries the panel shows come\n## from the schema, which is generated from the documentation and is not\n## translated, and the values it resolves — a color\'s derived word, a\n## function name, a type — come from the language server in the same state.\n## What is here is the panel\'s own prose around them (#1580).\n\nhelp-placeholder = Place cursor on a tag name, attribute, or { $ref } for documentation.\n\nhelp-unsupported-ref-chain = Help for multi-part references like { $example } is not yet supported.\n\nhelp-unresolved-ref =\n { $reason ->\n [notFound] No referent found for reference: { $ref }.\n [multiple] Multiple referents found for reference: { $ref }.\n *[indeterminate] A referent for { $ref } could not be determined.\n }\n\n# Both links end in an arrow, which is direction rather than punctuation and is\n# the same in every language this ships in — but it is inside the message, so a\n# language written right to left can turn it around.\nhelp-learn-about-references = Learn about references →\nhelp-reference-page = Reference page →\n\n# What can go where the cursor is. Two selectors joined: where the cursor sits,\n# and what the schema allows there.\nhelp-suggestions-header =\n { $location ->\n [inside] Inside { $element }\n *[top] At the top level\n }{ $allowed ->\n [none] { " — nothing goes here." }\n [text] { " — type text here." }\n [text-and-components] { " — type text here, or try:" }\n *[components] { " — things to try:" }\n }\n\n# `$shortcut` is a key combination and stays as written. `$total` is a real\n# count, so a language that agrees a noun with it can select on it.\nhelp-suggestions-footer = Press { $shortcut } to see all { $total } components.\n\n# An element\'s name joined to its one-line summary. `$name` is empty where the\n# panel has already printed the name beside this — a suggestion in the list —\n# and only the separator is wanted, so the message has to read as punctuation\n# on its own.\nhelp-name-summary = { $name } — { $summary }\n\n# `$line` is text, not a number: it identifies a line, so line 1234 is "1234"\n# and not "1,234". `none` is how a position that is not known selects a\n# sentence without one rather than substituting an empty parenthesis.\nhelp-ref-is-reference =\n { $line ->\n [none] { $ref } is a reference to { $target }.\n *[other] { $ref } is a reference to { $target } (line { $line }).\n }\n\nhelp-ref-derived-from =\n { $line ->\n [none] Introduced by { $owner } as { $role }.\n *[other] Introduced by { $owner } on line { $line } as { $role }.\n }\n\nhelp-property-is-reference =\n { $line ->\n [none] { $ref } is a reference to the { $property } property of { $element }.\n *[other] { $ref } is a reference to the { $property } property of { $element } (line { $line }).\n }\n\n# The badge on the title row saying what kind of thing the panel is explaining.\nhelp-kind-attribute = attribute\nhelp-kind-snippet = snippet\nhelp-kind-array-entry = array entry\n\nhelp-default = Default:\nhelp-active-default = Active default:\n\n# Appended after the active default\'s value. `styleNumber` is the attribute\'s\n# own name and stays as written; the space and brackets around it do not.\nhelp-style-number-annotation = { " " }(styleNumber { $styleNumber })\n\nhelp-allowed-values =\n { $perItem ->\n [true] Allowed values (one per item):\n *[other] Allowed values:\n }\n\n# Heading for values the editor offers without requiring: the attribute accepts\n# others too, and nothing warns about them. Distinct from `help-allowed-values`\n# so a translation can pick a word that suggests rather than constrains.\nhelp-suggested-values = Suggested values:\n\nhelp-inserts = Inserts:\n\nhelp-coordinates =\n { $count ->\n [one] Coordinate:\n *[other] Coordinates:\n }\n\nhelp-type = Type:\n\nhelp-resolved-style = Resolved style (styleNumber { $styleNumber }):\n\nhelp-resolved-function-names = Resolved function names:\nhelp-reset-list = Reset list on this input:\nhelp-added-on-input = Added on this input:\nhelp-removed-on-input = Removed on this input:\n\n# The three names are attributes an author writes, so they stay as written.\nhelp-reset-overrides = { $reset } overrides { $additional } and { $removed }.\n\n# Length syntax for a `componentSize` attribute (`width`, `height`). The values\n# listed alongside this label are examples the panel renders as chips.\nhelp-accepted-sizes = Accepted sizes:\nhelp-size-units = A bare number is pixels. A percentage is a share of the width around the component.\n\n# For a `height`, which is offered no percentage: the note must not raise a form\n# the attribute does not support.\nhelp-size-units-absolute = A bare number is pixels.\n\n# For a width that picks a named `size` preset (`<graph>`, `<image>`, `<video>`):\n# a percentage there is a share of the largest width any component may take, not\n# of the space around this one.\nhelp-size-units-preset = A bare number is pixels, and a percentage is a share of the widest a component can be.\n\n# For a side-by-side `width`, which is offered no absolute form: the same rule,\n# the other way round.\nhelp-size-units-relative = A percentage is a share of the width around the component.\n\n# `size` names a sibling attribute an author writes, so it stays as written.\nhelp-size-snaps-to-preset = This width picks the nearest { $size } preset rather than being used exactly.\n';
|
|
38637
38669
|
const CATALOG_NAMESPACES = [
|
|
38638
38670
|
"chrome",
|
|
@@ -42746,6 +42778,7 @@ const DIAGNOSTIC_CODES = {
|
|
|
42746
42778
|
"doenet-i0049": "index-operator-no-values",
|
|
42747
42779
|
"doenet-i0050": "tally-values-outside-categories",
|
|
42748
42780
|
"doenet-i0051": "attribute-value-invalid-ignoring",
|
|
42781
|
+
"doenet-i0052": "chart-histogram-values-outside-bins",
|
|
42749
42782
|
"doenet-w0001": "line-points-undetermined-dimensions",
|
|
42750
42783
|
"doenet-w0002": "line-points-too-few-dimensions",
|
|
42751
42784
|
"doenet-w0003": "line-points-depend-on-variables",
|
|
@@ -42899,6 +42932,12 @@ const DIAGNOSTIC_CODES = {
|
|
|
42899
42932
|
"doenet-w0153": "chart-pie-axis-name-ignored",
|
|
42900
42933
|
"doenet-w0154": "chart-box-values-not-drawable",
|
|
42901
42934
|
"doenet-w0155": "chart-box-categories-ignored",
|
|
42935
|
+
"doenet-w0156": "chart-histogram-values-not-drawable",
|
|
42936
|
+
"doenet-w0157": "chart-histogram-one-series",
|
|
42937
|
+
"doenet-w0158": "chart-histogram-bin-count-invalid",
|
|
42938
|
+
"doenet-w0159": "chart-histogram-cut-points-invalid",
|
|
42939
|
+
"doenet-w0160": "chart-histogram-categories-ignored",
|
|
42940
|
+
"doenet-w0161": "chart-histogram-bar-width-ignored",
|
|
42902
42941
|
"doenet-e0001": "pretzel-circuit-first-problem-distractor",
|
|
42903
42942
|
"doenet-e0002": "component-type-invalid",
|
|
42904
42943
|
"doenet-e0003": "attribute-repeated",
|
|
@@ -47317,7 +47356,7 @@ async function cidFromArrayBuffer(data) {
|
|
|
47317
47356
|
await crypto.subtle.digest("SHA-256", new Uint8Array());
|
|
47318
47357
|
digest = (data2) => crypto.subtle.digest("SHA-256", data2);
|
|
47319
47358
|
} catch (e22) {
|
|
47320
|
-
const sha256 = (await import("./sha256-8sXzDnE0-
|
|
47359
|
+
const sha256 = (await import("./sha256-8sXzDnE0-hQwEEL17-Br5jiMaf.js").then((n2) => n2.s)).default;
|
|
47321
47360
|
digest = (data2) => sha256(data2, {
|
|
47322
47361
|
asBytes: true
|
|
47323
47362
|
});
|
|
@@ -91932,6 +91971,54 @@ class CountingBaseListOperator extends CompositeComponent {
|
|
|
91932
91971
|
});
|
|
91933
91972
|
}
|
|
91934
91973
|
}
|
|
91974
|
+
function cutPointsAscend(edges) {
|
|
91975
|
+
return edges.every((edge, ind) => ind === 0 || edge >= edges[ind - 1]);
|
|
91976
|
+
}
|
|
91977
|
+
function countValuesInBins({
|
|
91978
|
+
values: values2,
|
|
91979
|
+
edges,
|
|
91980
|
+
closed
|
|
91981
|
+
}) {
|
|
91982
|
+
const sorted = values2.filter((value) => !Number.isNaN(value)).sort((a2, b2) => a2 - b2);
|
|
91983
|
+
function countBelow(x2) {
|
|
91984
|
+
let low = 0, high = sorted.length;
|
|
91985
|
+
while (low < high) {
|
|
91986
|
+
const mid = low + high >> 1;
|
|
91987
|
+
if (sorted[mid] < x2) {
|
|
91988
|
+
low = mid + 1;
|
|
91989
|
+
} else {
|
|
91990
|
+
high = mid;
|
|
91991
|
+
}
|
|
91992
|
+
}
|
|
91993
|
+
return low;
|
|
91994
|
+
}
|
|
91995
|
+
function countAtMost(x2) {
|
|
91996
|
+
let low = 0, high = sorted.length;
|
|
91997
|
+
while (low < high) {
|
|
91998
|
+
const mid = low + high >> 1;
|
|
91999
|
+
if (sorted[mid] <= x2) {
|
|
92000
|
+
low = mid + 1;
|
|
92001
|
+
} else {
|
|
92002
|
+
high = mid;
|
|
92003
|
+
}
|
|
92004
|
+
}
|
|
92005
|
+
return low;
|
|
92006
|
+
}
|
|
92007
|
+
const numBins = edges.length - 1;
|
|
92008
|
+
const counts = [];
|
|
92009
|
+
for (let bin = 0; bin < numBins; bin++) {
|
|
92010
|
+
const lower = edges[bin];
|
|
92011
|
+
const upper = edges[bin + 1];
|
|
92012
|
+
if (closed === "right") {
|
|
92013
|
+
const below = bin === 0 ? countBelow(lower) : countAtMost(lower);
|
|
92014
|
+
counts.push(countAtMost(upper) - below);
|
|
92015
|
+
} else {
|
|
92016
|
+
const atOrBelow = bin === numBins - 1 ? countAtMost(upper) : countBelow(upper);
|
|
92017
|
+
counts.push(atOrBelow - countBelow(lower));
|
|
92018
|
+
}
|
|
92019
|
+
}
|
|
92020
|
+
return counts;
|
|
92021
|
+
}
|
|
91935
92022
|
function labelForValue(value, numeric2) {
|
|
91936
92023
|
return numeric2 ? value.numericalValue : value.textValue;
|
|
91937
92024
|
}
|
|
@@ -92179,11 +92266,11 @@ class BinCounts extends CountingBaseListOperator {
|
|
|
92179
92266
|
validValues: [
|
|
92180
92267
|
{
|
|
92181
92268
|
value: "left",
|
|
92182
|
-
description: "Bins are `[a, b)`, matching NumPy and the usual textbook class interval. The last bin also includes its upper cut point, so
|
|
92269
|
+
description: "Bins are `[a, b)`, matching NumPy and the usual textbook class interval. The last bin also includes its upper cut point, so a value sitting exactly on it is counted."
|
|
92183
92270
|
},
|
|
92184
92271
|
{
|
|
92185
92272
|
value: "right",
|
|
92186
|
-
description: "Bins are `(a, b]`, matching R, pandas and Excel. The first bin also includes its lower cut point, so
|
|
92273
|
+
description: "Bins are `(a, b]`, matching R, pandas and Excel. The first bin also includes its lower cut point, so a value sitting exactly on it is counted."
|
|
92187
92274
|
}
|
|
92188
92275
|
]
|
|
92189
92276
|
};
|
|
@@ -92287,7 +92374,7 @@ function countInBins({ values: values2, numeric: numeric2, edges, closed }) {
|
|
|
92287
92374
|
})
|
|
92288
92375
|
);
|
|
92289
92376
|
}
|
|
92290
|
-
if (!
|
|
92377
|
+
if (!cutPointsAscend(edges)) {
|
|
92291
92378
|
return noBins(
|
|
92292
92379
|
codedDiagnostic({ type: "warning", code: "doenet-w0141" })
|
|
92293
92380
|
);
|
|
@@ -92305,44 +92392,11 @@ function countInBins({ values: values2, numeric: numeric2, edges, closed }) {
|
|
|
92305
92392
|
]
|
|
92306
92393
|
};
|
|
92307
92394
|
}
|
|
92308
|
-
const
|
|
92309
|
-
|
|
92310
|
-
|
|
92311
|
-
|
|
92312
|
-
|
|
92313
|
-
if (sorted[mid] < x2) {
|
|
92314
|
-
low = mid + 1;
|
|
92315
|
-
} else {
|
|
92316
|
-
high = mid;
|
|
92317
|
-
}
|
|
92318
|
-
}
|
|
92319
|
-
return low;
|
|
92320
|
-
}
|
|
92321
|
-
function countAtMost(x2) {
|
|
92322
|
-
let low = 0, high = sorted.length;
|
|
92323
|
-
while (low < high) {
|
|
92324
|
-
const mid = low + high >> 1;
|
|
92325
|
-
if (sorted[mid] <= x2) {
|
|
92326
|
-
low = mid + 1;
|
|
92327
|
-
} else {
|
|
92328
|
-
high = mid;
|
|
92329
|
-
}
|
|
92330
|
-
}
|
|
92331
|
-
return low;
|
|
92332
|
-
}
|
|
92333
|
-
const numBins = edges.length - 1;
|
|
92334
|
-
const counts = [];
|
|
92335
|
-
for (let bin = 0; bin < numBins; bin++) {
|
|
92336
|
-
const lower = edges[bin];
|
|
92337
|
-
const upper = edges[bin + 1];
|
|
92338
|
-
if (closed === "right") {
|
|
92339
|
-
const below = bin === 0 ? countBelow(lower) : countAtMost(lower);
|
|
92340
|
-
counts.push(countAtMost(upper) - below);
|
|
92341
|
-
} else {
|
|
92342
|
-
const atOrBelow = bin === numBins - 1 ? countAtMost(upper) : countBelow(upper);
|
|
92343
|
-
counts.push(atOrBelow - countBelow(lower));
|
|
92344
|
-
}
|
|
92345
|
-
}
|
|
92395
|
+
const counts = countValuesInBins({
|
|
92396
|
+
values: values2.map((value) => value.numericalValue),
|
|
92397
|
+
edges,
|
|
92398
|
+
closed
|
|
92399
|
+
});
|
|
92346
92400
|
return { counts, labels: null };
|
|
92347
92401
|
}
|
|
92348
92402
|
const CountOperators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
@@ -93471,17 +93525,14 @@ function saturatingAdd(total, value) {
|
|
|
93471
93525
|
}
|
|
93472
93526
|
return sum2 > 0 ? Number.MAX_VALUE : -Number.MAX_VALUE;
|
|
93473
93527
|
}
|
|
93474
|
-
function niceTickStep(span, minStep) {
|
|
93528
|
+
function niceTickStep(span, minStep, intervals = TARGET_TICK_INTERVALS) {
|
|
93475
93529
|
if (Number.isNaN(span) || span <= 0) {
|
|
93476
93530
|
return Math.max(minStep, 1);
|
|
93477
93531
|
}
|
|
93478
93532
|
if (!Number.isFinite(span)) {
|
|
93479
|
-
return Math.max(
|
|
93480
|
-
minStep,
|
|
93481
|
-
snapNumber(Number.MAX_VALUE / TARGET_TICK_INTERVALS)
|
|
93482
|
-
);
|
|
93533
|
+
return Math.max(minStep, snapNumber(Number.MAX_VALUE / intervals));
|
|
93483
93534
|
}
|
|
93484
|
-
const rough = span /
|
|
93535
|
+
const rough = span / intervals;
|
|
93485
93536
|
const magnitude = 10 ** Math.floor(Math.log10(rough));
|
|
93486
93537
|
if (!(magnitude > 0)) {
|
|
93487
93538
|
return Math.max(minStep, rough) || Number.MIN_VALUE;
|
|
@@ -94951,8 +95002,8 @@ function createBoxChartPrefigureXML({
|
|
|
94951
95002
|
xTicks: null,
|
|
94952
95003
|
// A box chart's positions are named by the series' own `<label>`, which
|
|
94953
95004
|
// may hold an `<m>` — the one type whose axis names can carry math,
|
|
94954
|
-
// since
|
|
94955
|
-
// `textList
|
|
95005
|
+
// since a chart of categories names its positions from `categories`, a
|
|
95006
|
+
// `textList`, and a histogram names its bins by their cut points. Read off the rendering rather than the geometry, which is
|
|
94956
95007
|
// renderer-neutral and has no business knowing how a name is typeset.
|
|
94957
95008
|
// One slot per series, in the same order, so the indices line up.
|
|
94958
95009
|
slots: geometry.slots.map((slot, seriesIndex) => ({
|
|
@@ -95001,6 +95052,322 @@ function createBoxChartPrefigureXML({
|
|
|
95001
95052
|
});
|
|
95002
95053
|
return { xml, diagnostics: diagnostics2 };
|
|
95003
95054
|
}
|
|
95055
|
+
const MAX_REQUESTED_BINS = 1e3;
|
|
95056
|
+
const TARGET_EDGE_LABELS = 6;
|
|
95057
|
+
const MAX_EDGE_LABELS = 16;
|
|
95058
|
+
function sturgesBinCount(count) {
|
|
95059
|
+
return Math.max(1, Math.ceil(Math.log2(count)) + 1);
|
|
95060
|
+
}
|
|
95061
|
+
function edgesFrom(lowest, width, count) {
|
|
95062
|
+
const edges = [snapNumber(lowest)];
|
|
95063
|
+
for (let ind = 1; ind <= count; ind++) {
|
|
95064
|
+
const edge = snapNumber(lowest + ind * width);
|
|
95065
|
+
if (!Number.isFinite(edge) || edge <= edges[edges.length - 1]) {
|
|
95066
|
+
break;
|
|
95067
|
+
}
|
|
95068
|
+
edges.push(edge);
|
|
95069
|
+
}
|
|
95070
|
+
return edges;
|
|
95071
|
+
}
|
|
95072
|
+
function widthWithoutSpread(value, minStep) {
|
|
95073
|
+
return niceTickStep(Math.abs(value) || 1, minStep);
|
|
95074
|
+
}
|
|
95075
|
+
function automaticEdges(observations, minStep) {
|
|
95076
|
+
const low = observations.reduce((a2, c2) => c2 < a2 ? c2 : a2);
|
|
95077
|
+
const high = observations.reduce((a2, c2) => c2 > a2 ? c2 : a2);
|
|
95078
|
+
const width = high > low ? niceTickStep(
|
|
95079
|
+
high - low,
|
|
95080
|
+
minStep,
|
|
95081
|
+
sturgesBinCount(observations.length)
|
|
95082
|
+
) : widthWithoutSpread(low, minStep);
|
|
95083
|
+
const lowest = tickAtOrBeyond(low, width, -1);
|
|
95084
|
+
const edges = edgesFrom(
|
|
95085
|
+
lowest,
|
|
95086
|
+
width,
|
|
95087
|
+
Math.max(1, Math.ceil(snapNumber((high - lowest) / width))) + 1
|
|
95088
|
+
);
|
|
95089
|
+
while (edges.length > 2 && edges[edges.length - 2] >= high) {
|
|
95090
|
+
edges.pop();
|
|
95091
|
+
}
|
|
95092
|
+
if (edges.length < 2) {
|
|
95093
|
+
return [low, high];
|
|
95094
|
+
}
|
|
95095
|
+
if (edges[0] > low) {
|
|
95096
|
+
edges[0] = low;
|
|
95097
|
+
}
|
|
95098
|
+
if (edges[edges.length - 1] < high) {
|
|
95099
|
+
edges[edges.length - 1] = high;
|
|
95100
|
+
}
|
|
95101
|
+
return edges;
|
|
95102
|
+
}
|
|
95103
|
+
function requestedEdges(observations, binCount, minStep) {
|
|
95104
|
+
const low = observations.reduce((a2, c2) => c2 < a2 ? c2 : a2);
|
|
95105
|
+
const high = observations.reduce((a2, c2) => c2 > a2 ? c2 : a2);
|
|
95106
|
+
if (!(high > low)) {
|
|
95107
|
+
const width = widthWithoutSpread(low, minStep);
|
|
95108
|
+
const edges2 = edgesFrom(
|
|
95109
|
+
tickAtOrBeyond(low, width, -1),
|
|
95110
|
+
width / binCount,
|
|
95111
|
+
binCount
|
|
95112
|
+
);
|
|
95113
|
+
if (edges2.length > 1 && edges2[0] <= low && edges2[edges2.length - 1] >= high) {
|
|
95114
|
+
return edges2;
|
|
95115
|
+
}
|
|
95116
|
+
return [low, high];
|
|
95117
|
+
}
|
|
95118
|
+
const edges = [low];
|
|
95119
|
+
for (let ind = 1; ind < binCount; ind++) {
|
|
95120
|
+
const fraction2 = ind / binCount;
|
|
95121
|
+
edges.push(snapNumber(low * (1 - fraction2) + high * fraction2));
|
|
95122
|
+
}
|
|
95123
|
+
edges.push(high);
|
|
95124
|
+
return edges.every((edge, ind) => ind === 0 || edge > edges[ind - 1]) ? edges : [edges[0], edges[edges.length - 1]];
|
|
95125
|
+
}
|
|
95126
|
+
function edgeTicks(edges, [xMin, xMax], wholeEdges, edgesChosenByChart) {
|
|
95127
|
+
function niceTicks() {
|
|
95128
|
+
return axisTicks(xMin, xMax, tickStepForBounds(xMin, xMax, wholeEdges));
|
|
95129
|
+
}
|
|
95130
|
+
if (edges.length < 2) {
|
|
95131
|
+
return niceTicks();
|
|
95132
|
+
}
|
|
95133
|
+
const exactWidth = edges[1] - edges[0];
|
|
95134
|
+
const width = snapNumber(exactWidth);
|
|
95135
|
+
const scale = edges.reduce(
|
|
95136
|
+
(largest2, edge) => Math.max(largest2, Math.abs(edge)),
|
|
95137
|
+
0
|
|
95138
|
+
);
|
|
95139
|
+
const slackPerWidth = scale * (edgesChosenByChart ? 1e-11 : 1e-14);
|
|
95140
|
+
const uniform = edges.every(
|
|
95141
|
+
(edge, ind) => ind === 0 || Math.abs(edge - edges[ind - 1] - exactWidth) <= slackPerWidth
|
|
95142
|
+
);
|
|
95143
|
+
if (!uniform || !(width > 0)) {
|
|
95144
|
+
return niceTicks();
|
|
95145
|
+
}
|
|
95146
|
+
const stride = Math.max(
|
|
95147
|
+
1,
|
|
95148
|
+
Math.ceil((edges.length - 1) / TARGET_EDGE_LABELS)
|
|
95149
|
+
);
|
|
95150
|
+
const step = snapNumber(width * stride);
|
|
95151
|
+
if (!(step > 0)) {
|
|
95152
|
+
return niceTicks();
|
|
95153
|
+
}
|
|
95154
|
+
const stepsToEdge = (value, round2) => {
|
|
95155
|
+
const ratio = (value - edges[0]) / step;
|
|
95156
|
+
const nearest = Math.round(ratio);
|
|
95157
|
+
const slack = Math.abs(ratio) * 1e-9 + Math.abs(ratio) * slackPerWidth / Math.abs(width) + 1e-9;
|
|
95158
|
+
return Math.abs(ratio - nearest) <= slack ? nearest : round2(ratio);
|
|
95159
|
+
};
|
|
95160
|
+
const firstIndex = stepsToEdge(xMin, Math.ceil);
|
|
95161
|
+
const lastIndex = stepsToEdge(xMax, Math.floor);
|
|
95162
|
+
if (!(lastIndex > firstIndex) || lastIndex - firstIndex + 1 > MAX_EDGE_LABELS) {
|
|
95163
|
+
return niceTicks();
|
|
95164
|
+
}
|
|
95165
|
+
const labelFor = (index2) => {
|
|
95166
|
+
const edgeIndex = index2 * stride;
|
|
95167
|
+
return edgeIndex >= 0 && edgeIndex < edges.length ? edges[edgeIndex] : snapNumber(edges[0] + index2 * step);
|
|
95168
|
+
};
|
|
95169
|
+
return {
|
|
95170
|
+
first: Math.max(labelFor(firstIndex), xMin),
|
|
95171
|
+
last: Math.min(labelFor(lastIndex), xMax),
|
|
95172
|
+
step
|
|
95173
|
+
};
|
|
95174
|
+
}
|
|
95175
|
+
function computeHistogramChartGeometry({
|
|
95176
|
+
series,
|
|
95177
|
+
bins,
|
|
95178
|
+
closed,
|
|
95179
|
+
xMinAttr,
|
|
95180
|
+
xMaxAttr,
|
|
95181
|
+
yMinAttr,
|
|
95182
|
+
yMaxAttr
|
|
95183
|
+
}) {
|
|
95184
|
+
const drawnSeries = series.slice(0, 1);
|
|
95185
|
+
const observations = [];
|
|
95186
|
+
let undrawnValues = 0;
|
|
95187
|
+
for (const value of drawnSeries[0]?.values ?? []) {
|
|
95188
|
+
if (Number.isFinite(value)) {
|
|
95189
|
+
observations.push(value);
|
|
95190
|
+
} else {
|
|
95191
|
+
undrawnValues++;
|
|
95192
|
+
}
|
|
95193
|
+
}
|
|
95194
|
+
const wholeValues = observations.every((value) => Number.isInteger(value));
|
|
95195
|
+
const minStep = wholeValues ? 1 : 0;
|
|
95196
|
+
const requestedCount = bins?.length === 1 ? bins[0] : null;
|
|
95197
|
+
const requestedEdgePoints = (bins?.length ?? 0) > 1 ? bins : null;
|
|
95198
|
+
let binsProblem = null;
|
|
95199
|
+
if (requestedCount !== null && !(Number.isInteger(requestedCount) && requestedCount >= 1 && requestedCount <= MAX_REQUESTED_BINS)) {
|
|
95200
|
+
binsProblem = "count";
|
|
95201
|
+
}
|
|
95202
|
+
if (requestedEdgePoints !== null && (!requestedEdgePoints.every((edge) => Number.isFinite(edge)) || !cutPointsAscend(requestedEdgePoints))) {
|
|
95203
|
+
binsProblem = "cutPoints";
|
|
95204
|
+
}
|
|
95205
|
+
let edges;
|
|
95206
|
+
if (drawnSeries.length === 0) {
|
|
95207
|
+
edges = [];
|
|
95208
|
+
} else if (observations.length === 0) {
|
|
95209
|
+
edges = binsProblem === null && requestedEdgePoints !== null ? requestedEdgePoints : [];
|
|
95210
|
+
} else if (binsProblem !== null) {
|
|
95211
|
+
edges = automaticEdges(observations, minStep);
|
|
95212
|
+
} else if (requestedEdgePoints !== null) {
|
|
95213
|
+
edges = requestedEdgePoints;
|
|
95214
|
+
} else if (requestedCount !== null) {
|
|
95215
|
+
edges = requestedEdges(observations, requestedCount, minStep);
|
|
95216
|
+
} else {
|
|
95217
|
+
edges = automaticEdges(observations, minStep);
|
|
95218
|
+
}
|
|
95219
|
+
const counts = edges.length > 1 ? countValuesInBins({ values: observations, edges, closed }) : [];
|
|
95220
|
+
const binGeometry = counts.map((count, index2) => ({
|
|
95221
|
+
index: index2,
|
|
95222
|
+
lower: edges[index2],
|
|
95223
|
+
upper: edges[index2 + 1],
|
|
95224
|
+
count
|
|
95225
|
+
}));
|
|
95226
|
+
const uncountedValues = observations.length - counts.reduce((total, count) => total + count, 0);
|
|
95227
|
+
const tallest = counts.reduce((a2, c2) => c2 > a2 ? c2 : a2, 0);
|
|
95228
|
+
const [autoYMin, autoYMax] = autoAxisBounds({
|
|
95229
|
+
low: 0,
|
|
95230
|
+
high: tallest,
|
|
95231
|
+
minStep: 1,
|
|
95232
|
+
baseline: 0
|
|
95233
|
+
});
|
|
95234
|
+
const [yMin, yMax] = reconcileBounds(
|
|
95235
|
+
yMinAttr,
|
|
95236
|
+
yMaxAttr,
|
|
95237
|
+
autoYMin,
|
|
95238
|
+
autoYMax
|
|
95239
|
+
);
|
|
95240
|
+
const lastEdge = edges[edges.length - 1];
|
|
95241
|
+
const [autoXMin, autoXMax] = edges.length < 2 ? [0, 1] : lastEdge > edges[0] ? [edges[0], lastEdge] : autoAxisBounds({
|
|
95242
|
+
low: edges[0],
|
|
95243
|
+
high: lastEdge,
|
|
95244
|
+
minStep,
|
|
95245
|
+
baseline: null
|
|
95246
|
+
});
|
|
95247
|
+
const [xMin, xMax] = reconcileBounds(
|
|
95248
|
+
xMinAttr,
|
|
95249
|
+
xMaxAttr,
|
|
95250
|
+
autoXMin,
|
|
95251
|
+
autoXMax
|
|
95252
|
+
);
|
|
95253
|
+
const wholeEdges = edges.every((edge) => Number.isInteger(edge));
|
|
95254
|
+
return {
|
|
95255
|
+
kind: "histogram",
|
|
95256
|
+
series: drawnSeries.map(({ label }) => ({ label })),
|
|
95257
|
+
bins: binGeometry,
|
|
95258
|
+
edges,
|
|
95259
|
+
bounds: [xMin, yMin, xMax, yMax],
|
|
95260
|
+
tickStep: tickStepForBounds(yMin, yMax, true),
|
|
95261
|
+
// Whether the cut points are ours or the author's decides how exactly
|
|
95262
|
+
// the axis holds them to being of one width: ours carry a twelve-digit
|
|
95263
|
+
// rounding, and theirs carry none.
|
|
95264
|
+
xTicks: edgeTicks(
|
|
95265
|
+
edges,
|
|
95266
|
+
[xMin, xMax],
|
|
95267
|
+
wholeEdges,
|
|
95268
|
+
binsProblem !== null || requestedEdgePoints === null
|
|
95269
|
+
),
|
|
95270
|
+
undrawnValues,
|
|
95271
|
+
uncountedValues,
|
|
95272
|
+
undrawnSeries: Math.max(series.length - 1, 0),
|
|
95273
|
+
binsProblem
|
|
95274
|
+
};
|
|
95275
|
+
}
|
|
95276
|
+
function binWidth(lower, upper) {
|
|
95277
|
+
const snapped = snapNumber(upper - lower);
|
|
95278
|
+
if (Number.isFinite(lower + snapped)) {
|
|
95279
|
+
return snapped;
|
|
95280
|
+
}
|
|
95281
|
+
return lower > 0 ? Number.MAX_VALUE - lower : Number.MAX_VALUE;
|
|
95282
|
+
}
|
|
95283
|
+
function createHistogramChartPrefigureXML({
|
|
95284
|
+
geometry,
|
|
95285
|
+
seriesRendering,
|
|
95286
|
+
binAnnotationText,
|
|
95287
|
+
widthPx,
|
|
95288
|
+
heightPx,
|
|
95289
|
+
xLabel,
|
|
95290
|
+
xLabelHasLatex,
|
|
95291
|
+
yLabel,
|
|
95292
|
+
yLabelHasLatex,
|
|
95293
|
+
title,
|
|
95294
|
+
showLegend,
|
|
95295
|
+
legendPosition,
|
|
95296
|
+
displayValues,
|
|
95297
|
+
shortDescription,
|
|
95298
|
+
darkMode = false
|
|
95299
|
+
}) {
|
|
95300
|
+
const diagnostics2 = [];
|
|
95301
|
+
const [, yMin, , yMax] = geometry.bounds;
|
|
95302
|
+
const barAttrs = geometry.series.length ? styleAttributes({
|
|
95303
|
+
selectedStyle: seriesRendering[0]?.selectedStyle,
|
|
95304
|
+
diagnostics: diagnostics2,
|
|
95305
|
+
warningPrefix: "<chart>"
|
|
95306
|
+
}).join(" ") : "";
|
|
95307
|
+
const elements = [];
|
|
95308
|
+
const annotations = [];
|
|
95309
|
+
let keyHandle = null;
|
|
95310
|
+
const valueLabelElements = [];
|
|
95311
|
+
for (const bin of geometry.bins) {
|
|
95312
|
+
const handle = `bin-${bin.index + 1}`;
|
|
95313
|
+
if (keyHandle === null) {
|
|
95314
|
+
keyHandle = handle;
|
|
95315
|
+
}
|
|
95316
|
+
const width = binWidth(bin.lower, bin.upper);
|
|
95317
|
+
const lowerLeft = `(${formatNumber$1(bin.lower)},0)`;
|
|
95318
|
+
const dimensions = `(${formatNumber$1(width)},${formatNumber$1(bin.count)})`;
|
|
95319
|
+
elements.push(
|
|
95320
|
+
`<rectangle at="${escapeXml(handle)}" lower-left="${escapeXml(lowerLeft)}" dimensions="${escapeXml(dimensions)}" cliptobbox="yes"${barAttrs ? ` ${barAttrs}` : ""} />`
|
|
95321
|
+
);
|
|
95322
|
+
if (displayValues) {
|
|
95323
|
+
const anchor = `(${formatNumber$1(snapNumber(bin.lower + width / 2))},${formatNumber$1(bin.count)})`;
|
|
95324
|
+
valueLabelElements.push(
|
|
95325
|
+
`<label anchor="${escapeXml(anchor)}" alignment="north" ${THEME_AWARE_LABEL_COLOR_ATTR}>${escapeXml(formatNumber$1(bin.count) ?? "")}</label>`
|
|
95326
|
+
);
|
|
95327
|
+
}
|
|
95328
|
+
annotations.push(
|
|
95329
|
+
`<annotation ref="${escapeXml(handle)}" text="${escapeXml(
|
|
95330
|
+
binAnnotationText({
|
|
95331
|
+
from: formatNumber$1(bin.lower) ?? "",
|
|
95332
|
+
to: formatNumber$1(bin.upper) ?? "",
|
|
95333
|
+
count: formatNumber$1(bin.count) ?? ""
|
|
95334
|
+
})
|
|
95335
|
+
)}" />`
|
|
95336
|
+
);
|
|
95337
|
+
}
|
|
95338
|
+
const xml = assembleChartDiagram({
|
|
95339
|
+
bounds: geometry.bounds,
|
|
95340
|
+
yTicks: axisTicks(yMin, yMax, geometry.tickStep),
|
|
95341
|
+
xTicks: geometry.xTicks,
|
|
95342
|
+
// Numbered rather than named: the positions are stretches of a numeric
|
|
95343
|
+
// scale, which is what `slots` cannot express.
|
|
95344
|
+
slots: null,
|
|
95345
|
+
widthPx,
|
|
95346
|
+
heightPx,
|
|
95347
|
+
xLabel,
|
|
95348
|
+
xLabelHasLatex,
|
|
95349
|
+
yLabel,
|
|
95350
|
+
yLabelHasLatex,
|
|
95351
|
+
title,
|
|
95352
|
+
showLegend,
|
|
95353
|
+
legendPosition,
|
|
95354
|
+
seriesRendering,
|
|
95355
|
+
// The drawn series alone, so the bars are not wrapped in a `<group>`
|
|
95356
|
+
// for a screen reader to stop at: one group of data has no level
|
|
95357
|
+
// between the figure and its marks worth walking.
|
|
95358
|
+
seriesLabels: geometry.series.map(({ label }) => label),
|
|
95359
|
+
seriesElements: geometry.series.map(() => elements),
|
|
95360
|
+
seriesAnnotations: geometry.series.map(() => annotations),
|
|
95361
|
+
seriesKeyHandles: geometry.series.map(() => keyHandle),
|
|
95362
|
+
legendKeyWidth: LEGEND_SWATCH_KEY_WIDTH,
|
|
95363
|
+
// A bar is drawn inside the box and clipped to it.
|
|
95364
|
+
markOverhang: 0,
|
|
95365
|
+
overlayElements: valueLabelElements,
|
|
95366
|
+
shortDescription,
|
|
95367
|
+
darkMode
|
|
95368
|
+
});
|
|
95369
|
+
return { xml, diagnostics: diagnostics2 };
|
|
95370
|
+
}
|
|
95004
95371
|
function chartLegendHasItems(geometry) {
|
|
95005
95372
|
if (geometry === null) {
|
|
95006
95373
|
return false;
|
|
@@ -95008,6 +95375,14 @@ function chartLegendHasItems(geometry) {
|
|
|
95008
95375
|
if (geometry.kind === "box") {
|
|
95009
95376
|
return false;
|
|
95010
95377
|
}
|
|
95378
|
+
if (geometry.kind === "histogram") {
|
|
95379
|
+
return geometry.bins.length > 0 && geometry.series.some(
|
|
95380
|
+
(oneSeries) => labelMarkup({
|
|
95381
|
+
label: oneSeries.label,
|
|
95382
|
+
labelHasLatex: false
|
|
95383
|
+
}) !== null
|
|
95384
|
+
);
|
|
95385
|
+
}
|
|
95011
95386
|
if (geometry.kind === "pie") {
|
|
95012
95387
|
return geometry.slices.some(
|
|
95013
95388
|
(slice2) => slice2.sweep > 0 && labelMarkup({
|
|
@@ -95072,7 +95447,14 @@ function returnShortDescriptionStateVariableDefinition({
|
|
|
95072
95447
|
}
|
|
95073
95448
|
const DEFAULT_ASPECT_RATIO = 1.5;
|
|
95074
95449
|
const DEFAULT_BAR_WIDTH = 0.8;
|
|
95075
|
-
const DRAWABLE_TYPES = /* @__PURE__ */ new Set([
|
|
95450
|
+
const DRAWABLE_TYPES = /* @__PURE__ */ new Set([
|
|
95451
|
+
"bar",
|
|
95452
|
+
"line",
|
|
95453
|
+
"scatter",
|
|
95454
|
+
"pie",
|
|
95455
|
+
"box",
|
|
95456
|
+
"histogram"
|
|
95457
|
+
]);
|
|
95076
95458
|
class Chart extends BlockComponent {
|
|
95077
95459
|
constructor(args) {
|
|
95078
95460
|
super(args);
|
|
@@ -95119,6 +95501,35 @@ class Chart extends BlockComponent {
|
|
|
95119
95501
|
{
|
|
95120
95502
|
value: "box",
|
|
95121
95503
|
description: "A box plot per series, side by side under an axis of the series' names. A series holds raw observations rather than one value per category."
|
|
95504
|
+
},
|
|
95505
|
+
{
|
|
95506
|
+
value: "histogram",
|
|
95507
|
+
description: "A histogram: one bar per bin, adjacent with no gap, over a numeric axis of cut points. A series holds raw observations, and the bars' heights are how many fall in each bin. Draws one series."
|
|
95508
|
+
}
|
|
95509
|
+
]
|
|
95510
|
+
};
|
|
95511
|
+
attributes.bins = {
|
|
95512
|
+
groupName: "marks",
|
|
95513
|
+
createComponentOfType: "numberList",
|
|
95514
|
+
description: "How to divide a histogram's scale: a whole number of equal-width bins, from 1 to 1000, or two or more finite cut points, each at least as large as the one before it. Omit it and the bins are chosen from the data. Only a histogram draws with it.",
|
|
95515
|
+
highlighted: true
|
|
95516
|
+
};
|
|
95517
|
+
attributes.closed = {
|
|
95518
|
+
groupName: "marks",
|
|
95519
|
+
description: "Which end of each of a histogram's bins includes its cut point. Each outermost cut point belongs to its own bin whichever end is closed, so an observation sitting exactly on the first or the last of them is counted.",
|
|
95520
|
+
createComponentOfType: "text",
|
|
95521
|
+
createStateVariable: "closed",
|
|
95522
|
+
defaultValue: "left",
|
|
95523
|
+
public: true,
|
|
95524
|
+
toLowerCase: true,
|
|
95525
|
+
validValues: [
|
|
95526
|
+
{
|
|
95527
|
+
value: "left",
|
|
95528
|
+
description: "Bins are `[a, b)`, matching NumPy and the usual textbook class interval."
|
|
95529
|
+
},
|
|
95530
|
+
{
|
|
95531
|
+
value: "right",
|
|
95532
|
+
description: "Bins are `(a, b]`, matching R, pandas and Excel."
|
|
95122
95533
|
}
|
|
95123
95534
|
]
|
|
95124
95535
|
};
|
|
@@ -95131,7 +95542,7 @@ class Chart extends BlockComponent {
|
|
|
95131
95542
|
attributes.categories = {
|
|
95132
95543
|
groupName: "axes",
|
|
95133
95544
|
createComponentOfType: "textList",
|
|
95134
|
-
description: "The name of each position in the data: the label under each position on the horizontal axis, or the name of each slice of a pie. Defaults to the position itself, 1, 2, 3 and so on. A line or scatter chart whose series carry an `x` is placed by measurement instead and has no positions to name; a bar chart and a pie name theirs whatever else the series carry. A box plot's positions are its series, each named by its own `<label
|
|
95545
|
+
description: "The name of each position in the data: the label under each position on the horizontal axis, or the name of each slice of a pie. Defaults to the position itself, 1, 2, 3 and so on. A line or scatter chart whose series carry an `x` is placed by measurement instead and has no positions to name; a bar chart and a pie name theirs whatever else the series carry. A box plot's positions are its series, each named by its own `<label>`, and a histogram's are its bins, each named by the cut points it runs between.",
|
|
95135
95546
|
highlighted: true
|
|
95136
95547
|
};
|
|
95137
95548
|
attributes.barWidth = {
|
|
@@ -95209,35 +95620,35 @@ class Chart extends BlockComponent {
|
|
|
95209
95620
|
};
|
|
95210
95621
|
attributes.xMin = {
|
|
95211
95622
|
groupName: "axes",
|
|
95212
|
-
description: "Leftmost value shown on the horizontal axis, for a chart with a numeric one. Defaults to the first tick below the smallest `x
|
|
95623
|
+
description: "Leftmost value shown on the horizontal axis, for a chart with a numeric one. Defaults to the first tick below the smallest `x`, or on a histogram to its first cut point, since its bars fill the axis. If `xMin` and `xMax` do not describe a box — both finite, with `xMin` below `xMax` — the axis is chosen from the data instead. A pie has no axes and reads neither.",
|
|
95213
95624
|
createComponentOfType: "number",
|
|
95214
95625
|
createStateVariable: "xMinAttr",
|
|
95215
95626
|
defaultValue: null
|
|
95216
95627
|
};
|
|
95217
95628
|
attributes.xMax = {
|
|
95218
95629
|
groupName: "axes",
|
|
95219
|
-
description: "Rightmost value shown on the horizontal axis, for a chart with a numeric one. Defaults to the first tick above the largest `x
|
|
95630
|
+
description: "Rightmost value shown on the horizontal axis, for a chart with a numeric one. Defaults to the first tick above the largest `x`, or on a histogram to its last cut point, since its bars fill the axis. If `xMin` and `xMax` do not describe a box — both finite, with `xMin` below `xMax` — the axis is chosen from the data instead. A pie has no axes and reads neither.",
|
|
95220
95631
|
createComponentOfType: "number",
|
|
95221
95632
|
createStateVariable: "xMaxAttr",
|
|
95222
95633
|
defaultValue: null
|
|
95223
95634
|
};
|
|
95224
95635
|
attributes.yMin = {
|
|
95225
95636
|
groupName: "axes",
|
|
95226
|
-
description: "Lowest value shown on the vertical axis. Defaults to 0 for a bar chart, whose bars are measured from it, or to the first tick past the smallest value — which is what a bar chart with negative values gets, and what a line, scatter or box chart always gets. If `yMin` and `yMax` do not describe a box — both finite, with `yMin` below `yMax` — the axis is chosen from the data instead. A pie has no axes and reads neither.",
|
|
95637
|
+
description: "Lowest value shown on the vertical axis. Defaults to 0 for a bar chart, whose bars are measured from it, and for a histogram, whose bars are counts, or to the first tick past the smallest value — which is what a bar chart with negative values gets, and what a line, scatter or box chart always gets. If `yMin` and `yMax` do not describe a box — both finite, with `yMin` below `yMax` — the axis is chosen from the data instead. A pie has no axes and reads neither.",
|
|
95227
95638
|
createComponentOfType: "number",
|
|
95228
95639
|
createStateVariable: "yMinAttr",
|
|
95229
95640
|
defaultValue: null
|
|
95230
95641
|
};
|
|
95231
95642
|
attributes.yMax = {
|
|
95232
95643
|
groupName: "axes",
|
|
95233
|
-
description: "Highest value shown on the vertical axis. Defaults to the next tick above the largest value. If `yMin` and `yMax` do not describe a box — both finite, with `yMin` below `yMax` — the axis is chosen from the data instead. A pie has no axes and reads neither.",
|
|
95644
|
+
description: "Highest value shown on the vertical axis. Defaults to the next tick above the largest value, or on a histogram above the tallest count. If `yMin` and `yMax` do not describe a box — both finite, with `yMin` below `yMax` — the axis is chosen from the data instead. A pie has no axes and reads neither.",
|
|
95234
95645
|
createComponentOfType: "number",
|
|
95235
95646
|
createStateVariable: "yMaxAttr",
|
|
95236
95647
|
defaultValue: null
|
|
95237
95648
|
};
|
|
95238
95649
|
attributes.displayValues = {
|
|
95239
95650
|
groupName: "marks",
|
|
95240
|
-
description: "Whether to print each value beside its mark: at a bar's far end — above one that rises, below one that falls — above a line or scatter chart's point, and beyond the rim of a pie's slice. A box plot reports five numbers at each position rather than one, and does not draw with it.",
|
|
95651
|
+
description: "Whether to print each value beside its mark: at a bar's far end — above one that rises, below one that falls — above a line or scatter chart's point, and beyond the rim of a pie's slice. A histogram prints how many observations fell in each bin above its bar. A box plot reports five numbers at each position rather than one, and does not draw with it.",
|
|
95241
95652
|
createComponentOfType: "boolean",
|
|
95242
95653
|
createStateVariable: "displayValues",
|
|
95243
95654
|
defaultValue: false,
|
|
@@ -95561,7 +95972,7 @@ class Chart extends BlockComponent {
|
|
|
95561
95972
|
stateVariableDefinitions.numSeries = {
|
|
95562
95973
|
groupName: "data",
|
|
95563
95974
|
highlighted: true,
|
|
95564
|
-
description: "How many series the chart holds: its `<series>` children that are not hidden, or one for a chart of bare values. Not necessarily how many are drawn — a pie
|
|
95975
|
+
description: "How many series the chart holds: its `<series>` children that are not hidden, or one for a chart of bare values. Not necessarily how many are drawn — a pie and a histogram each draw the first of them and no more.",
|
|
95565
95976
|
public: true,
|
|
95566
95977
|
shadowingInstructions: {
|
|
95567
95978
|
createComponentOfType: "integer"
|
|
@@ -95621,7 +96032,7 @@ class Chart extends BlockComponent {
|
|
|
95621
96032
|
};
|
|
95622
96033
|
stateVariableDefinitions.categories = {
|
|
95623
96034
|
groupName: "axes",
|
|
95624
|
-
description: "The name of each position in the data, in order: the label under each bar, or the name of each slice of a pie. Empty on a box plot, whose positions are its series.",
|
|
96035
|
+
description: "The name of each position in the data, in order: the label under each bar, or the name of each slice of a pie. Empty on a box plot, whose positions are its series, and on a histogram, whose positions are its bins.",
|
|
95625
96036
|
public: true,
|
|
95626
96037
|
isArray: true,
|
|
95627
96038
|
entryPrefixes: ["category"],
|
|
@@ -95632,12 +96043,12 @@ class Chart extends BlockComponent {
|
|
|
95632
96043
|
// same categories, so a series that runs short leaves the later
|
|
95633
96044
|
// slots empty rather than shortening the axis under the others.
|
|
95634
96045
|
//
|
|
95635
|
-
// None at all on
|
|
95636
|
-
//
|
|
95637
|
-
//
|
|
95638
|
-
//
|
|
95639
|
-
// observation would name a hundred
|
|
95640
|
-
// three.
|
|
96046
|
+
// None at all on the two types whose series holds observations
|
|
96047
|
+
// rather than one value per category. A box plot's positions are
|
|
96048
|
+
// its series, named by their own labels; a histogram's are the bins
|
|
96049
|
+
// it divided the scale into, named by the numbers they run between.
|
|
96050
|
+
// Reporting one category per observation would name a hundred
|
|
96051
|
+
// positions on a chart that has three.
|
|
95641
96052
|
returnArraySizeDependencies: () => ({
|
|
95642
96053
|
type: {
|
|
95643
96054
|
dependencyType: "stateVariable",
|
|
@@ -95649,7 +96060,7 @@ class Chart extends BlockComponent {
|
|
|
95649
96060
|
}
|
|
95650
96061
|
}),
|
|
95651
96062
|
returnArraySize({ dependencyValues }) {
|
|
95652
|
-
if (dependencyValues.type === "box") {
|
|
96063
|
+
if (dependencyValues.type === "box" || dependencyValues.type === "histogram") {
|
|
95653
96064
|
return [0];
|
|
95654
96065
|
}
|
|
95655
96066
|
return [
|
|
@@ -95755,6 +96166,23 @@ class Chart extends BlockComponent {
|
|
|
95755
96166
|
dependencyType: "stateVariable",
|
|
95756
96167
|
variableName: "barWidth"
|
|
95757
96168
|
},
|
|
96169
|
+
// The private name the attribute is read into, so that
|
|
96170
|
+
// `usedDefault` can say whether a width was asked for at all —
|
|
96171
|
+
// which is what the histogram branch below has to know, the
|
|
96172
|
+
// public `barWidth` being 0.8 either way.
|
|
96173
|
+
barWidthAttr: {
|
|
96174
|
+
dependencyType: "stateVariable",
|
|
96175
|
+
variableName: "barWidthAttr"
|
|
96176
|
+
},
|
|
96177
|
+
binsAttr: {
|
|
96178
|
+
dependencyType: "attributeComponent",
|
|
96179
|
+
attributeName: "bins",
|
|
96180
|
+
variableNames: ["numbers"]
|
|
96181
|
+
},
|
|
96182
|
+
closed: {
|
|
96183
|
+
dependencyType: "stateVariable",
|
|
96184
|
+
variableName: "closed"
|
|
96185
|
+
},
|
|
95758
96186
|
layout: {
|
|
95759
96187
|
dependencyType: "stateVariable",
|
|
95760
96188
|
variableName: "layout"
|
|
@@ -95776,7 +96204,7 @@ class Chart extends BlockComponent {
|
|
|
95776
96204
|
variableName: "yMaxAttr"
|
|
95777
96205
|
}
|
|
95778
96206
|
}),
|
|
95779
|
-
definition({ dependencyValues }) {
|
|
96207
|
+
definition({ dependencyValues, usedDefault }) {
|
|
95780
96208
|
const { type } = dependencyValues;
|
|
95781
96209
|
if (!DRAWABLE_TYPES.has(type)) {
|
|
95782
96210
|
return {
|
|
@@ -95892,6 +96320,86 @@ class Chart extends BlockComponent {
|
|
|
95892
96320
|
sendDiagnostics
|
|
95893
96321
|
};
|
|
95894
96322
|
}
|
|
96323
|
+
if (type === "histogram") {
|
|
96324
|
+
const geometry2 = computeHistogramChartGeometry({
|
|
96325
|
+
series: dependencyValues.seriesData.map(
|
|
96326
|
+
({ label, values: values2 }) => ({ label, values: values2 })
|
|
96327
|
+
),
|
|
96328
|
+
bins: dependencyValues.binsAttr?.stateValues.numbers ?? null,
|
|
96329
|
+
closed: dependencyValues.closed,
|
|
96330
|
+
xMinAttr: dependencyValues.xMinAttr,
|
|
96331
|
+
xMaxAttr: dependencyValues.xMaxAttr,
|
|
96332
|
+
yMinAttr: dependencyValues.yMinAttr,
|
|
96333
|
+
yMaxAttr: dependencyValues.yMaxAttr
|
|
96334
|
+
});
|
|
96335
|
+
const sendDiagnostics = [];
|
|
96336
|
+
if (geometry2.undrawnValues > 0) {
|
|
96337
|
+
sendDiagnostics.push(
|
|
96338
|
+
codedDiagnostic({
|
|
96339
|
+
type: "warning",
|
|
96340
|
+
code: "doenet-w0156"
|
|
96341
|
+
})
|
|
96342
|
+
);
|
|
96343
|
+
}
|
|
96344
|
+
if (geometry2.undrawnSeries > 0) {
|
|
96345
|
+
sendDiagnostics.push(
|
|
96346
|
+
codedDiagnostic({
|
|
96347
|
+
type: "warning",
|
|
96348
|
+
code: "doenet-w0157"
|
|
96349
|
+
})
|
|
96350
|
+
);
|
|
96351
|
+
}
|
|
96352
|
+
if (geometry2.binsProblem === "count") {
|
|
96353
|
+
sendDiagnostics.push(
|
|
96354
|
+
codedDiagnostic({
|
|
96355
|
+
type: "warning",
|
|
96356
|
+
code: "doenet-w0158",
|
|
96357
|
+
args: {
|
|
96358
|
+
bins: String(
|
|
96359
|
+
dependencyValues.binsAttr?.stateValues.numbers[0]
|
|
96360
|
+
),
|
|
96361
|
+
maximum: String(MAX_REQUESTED_BINS)
|
|
96362
|
+
}
|
|
96363
|
+
})
|
|
96364
|
+
);
|
|
96365
|
+
}
|
|
96366
|
+
if (geometry2.binsProblem === "cutPoints") {
|
|
96367
|
+
sendDiagnostics.push(
|
|
96368
|
+
codedDiagnostic({
|
|
96369
|
+
type: "warning",
|
|
96370
|
+
code: "doenet-w0159"
|
|
96371
|
+
})
|
|
96372
|
+
);
|
|
96373
|
+
}
|
|
96374
|
+
if (geometry2.uncountedValues > 0) {
|
|
96375
|
+
sendDiagnostics.push(
|
|
96376
|
+
codedDiagnostic({
|
|
96377
|
+
type: "info",
|
|
96378
|
+
code: "doenet-i0052"
|
|
96379
|
+
})
|
|
96380
|
+
);
|
|
96381
|
+
}
|
|
96382
|
+
if (dependencyValues.categoriesAttr?.stateValues.texts?.length > 0) {
|
|
96383
|
+
sendDiagnostics.push(
|
|
96384
|
+
codedDiagnostic({
|
|
96385
|
+
type: "warning",
|
|
96386
|
+
code: "doenet-w0160"
|
|
96387
|
+
})
|
|
96388
|
+
);
|
|
96389
|
+
}
|
|
96390
|
+
if (!usedDefault.barWidthAttr) {
|
|
96391
|
+
sendDiagnostics.push(
|
|
96392
|
+
codedDiagnostic({
|
|
96393
|
+
type: "warning",
|
|
96394
|
+
code: "doenet-w0161"
|
|
96395
|
+
})
|
|
96396
|
+
);
|
|
96397
|
+
}
|
|
96398
|
+
return {
|
|
96399
|
+
setValue: { chartGeometry: geometry2 },
|
|
96400
|
+
sendDiagnostics
|
|
96401
|
+
};
|
|
96402
|
+
}
|
|
95895
96403
|
const geometry = computePointChartGeometry({
|
|
95896
96404
|
series: dependencyValues.seriesData.map(
|
|
95897
96405
|
({ label, values: values2, x: x2 }) => ({ label, values: values2, x: x2 })
|
|
@@ -95984,7 +96492,7 @@ class Chart extends BlockComponent {
|
|
|
95984
96492
|
}),
|
|
95985
96493
|
definition({ dependencyValues }) {
|
|
95986
96494
|
const geometry = dependencyValues.chartGeometry;
|
|
95987
|
-
if (geometry?.kind !== "point" || geometry.slots) {
|
|
96495
|
+
if (geometry?.kind !== "histogram" && (geometry?.kind !== "point" || geometry.slots)) {
|
|
95988
96496
|
return { setValue: { xMin: null, xMax: null } };
|
|
95989
96497
|
}
|
|
95990
96498
|
const [xMin, , xMax] = geometry.bounds;
|
|
@@ -96024,6 +96532,86 @@ class Chart extends BlockComponent {
|
|
|
96024
96532
|
return { setValue: { yMin, yMax } };
|
|
96025
96533
|
}
|
|
96026
96534
|
};
|
|
96535
|
+
stateVariableDefinitions.binEdges = {
|
|
96536
|
+
groupName: "data",
|
|
96537
|
+
highlighted: true,
|
|
96538
|
+
description: "The cut points a histogram's bins run between, in order: one more than the number of bins. Empty for every other type.",
|
|
96539
|
+
public: true,
|
|
96540
|
+
isArray: true,
|
|
96541
|
+
entryPrefixes: ["binEdge"],
|
|
96542
|
+
shadowingInstructions: {
|
|
96543
|
+
createComponentOfType: "number"
|
|
96544
|
+
},
|
|
96545
|
+
returnArraySizeDependencies: () => ({
|
|
96546
|
+
chartGeometry: {
|
|
96547
|
+
dependencyType: "stateVariable",
|
|
96548
|
+
variableName: "chartGeometry"
|
|
96549
|
+
}
|
|
96550
|
+
}),
|
|
96551
|
+
returnArraySize({ dependencyValues }) {
|
|
96552
|
+
const geometry = dependencyValues.chartGeometry;
|
|
96553
|
+
return [
|
|
96554
|
+
geometry?.kind === "histogram" ? geometry.edges.length : 0
|
|
96555
|
+
];
|
|
96556
|
+
},
|
|
96557
|
+
returnArrayDependenciesByKey: () => ({
|
|
96558
|
+
globalDependencies: {
|
|
96559
|
+
chartGeometry: {
|
|
96560
|
+
dependencyType: "stateVariable",
|
|
96561
|
+
variableName: "chartGeometry"
|
|
96562
|
+
}
|
|
96563
|
+
}
|
|
96564
|
+
}),
|
|
96565
|
+
arrayDefinitionByKey({ globalDependencyValues, arrayKeys }) {
|
|
96566
|
+
const geometry = globalDependencyValues.chartGeometry;
|
|
96567
|
+
const edges = geometry?.kind === "histogram" ? geometry.edges : [];
|
|
96568
|
+
const binEdges = {};
|
|
96569
|
+
for (const arrayKey of arrayKeys) {
|
|
96570
|
+
binEdges[arrayKey] = edges[arrayKey];
|
|
96571
|
+
}
|
|
96572
|
+
return { setValue: { binEdges } };
|
|
96573
|
+
}
|
|
96574
|
+
};
|
|
96575
|
+
stateVariableDefinitions.binCounts = {
|
|
96576
|
+
groupName: "data",
|
|
96577
|
+
highlighted: true,
|
|
96578
|
+
description: "How many observations fell in each of a histogram's bins, in order — the height of each bar. Empty for every other type.",
|
|
96579
|
+
public: true,
|
|
96580
|
+
isArray: true,
|
|
96581
|
+
entryPrefixes: ["binCount"],
|
|
96582
|
+
shadowingInstructions: {
|
|
96583
|
+
createComponentOfType: "number"
|
|
96584
|
+
},
|
|
96585
|
+
returnArraySizeDependencies: () => ({
|
|
96586
|
+
chartGeometry: {
|
|
96587
|
+
dependencyType: "stateVariable",
|
|
96588
|
+
variableName: "chartGeometry"
|
|
96589
|
+
}
|
|
96590
|
+
}),
|
|
96591
|
+
returnArraySize({ dependencyValues }) {
|
|
96592
|
+
const geometry = dependencyValues.chartGeometry;
|
|
96593
|
+
return [
|
|
96594
|
+
geometry?.kind === "histogram" ? geometry.bins.length : 0
|
|
96595
|
+
];
|
|
96596
|
+
},
|
|
96597
|
+
returnArrayDependenciesByKey: () => ({
|
|
96598
|
+
globalDependencies: {
|
|
96599
|
+
chartGeometry: {
|
|
96600
|
+
dependencyType: "stateVariable",
|
|
96601
|
+
variableName: "chartGeometry"
|
|
96602
|
+
}
|
|
96603
|
+
}
|
|
96604
|
+
}),
|
|
96605
|
+
arrayDefinitionByKey({ globalDependencyValues, arrayKeys }) {
|
|
96606
|
+
const geometry = globalDependencyValues.chartGeometry;
|
|
96607
|
+
const bins = geometry?.kind === "histogram" ? geometry.bins : [];
|
|
96608
|
+
const binCounts = {};
|
|
96609
|
+
for (const arrayKey of arrayKeys) {
|
|
96610
|
+
binCounts[arrayKey] = bins[arrayKey]?.count;
|
|
96611
|
+
}
|
|
96612
|
+
return { setValue: { binCounts } };
|
|
96613
|
+
}
|
|
96614
|
+
};
|
|
96027
96615
|
stateVariableDefinitions.prefigureXML = {
|
|
96028
96616
|
description: "The PreFigure-formatted XML rendered for this chart, or null when there is no chart to draw.",
|
|
96029
96617
|
public: true,
|
|
@@ -96193,6 +96781,34 @@ class Chart extends BlockComponent {
|
|
|
96193
96781
|
sendDiagnostics: diagnostics3
|
|
96194
96782
|
};
|
|
96195
96783
|
}
|
|
96784
|
+
if (dependencyValues.type === "histogram") {
|
|
96785
|
+
const t22 = contentTranslator(dependencyValues);
|
|
96786
|
+
const { xml: xml2, diagnostics: diagnostics3 } = createHistogramChartPrefigureXML({
|
|
96787
|
+
geometry: dependencyValues.chartGeometry,
|
|
96788
|
+
seriesRendering,
|
|
96789
|
+
// The two cut points and the count, with the count
|
|
96790
|
+
// named: a bar's height is a number of things, and
|
|
96791
|
+
// three bare numbers in a row say nothing about
|
|
96792
|
+
// which of them is which.
|
|
96793
|
+
binAnnotationText: (parts) => t22("chart-histogram-bin", parts),
|
|
96794
|
+
widthPx,
|
|
96795
|
+
heightPx,
|
|
96796
|
+
xLabel: dependencyValues.xLabel,
|
|
96797
|
+
xLabelHasLatex: dependencyValues.xLabelHasLatex,
|
|
96798
|
+
yLabel: dependencyValues.yLabel,
|
|
96799
|
+
yLabelHasLatex: dependencyValues.yLabelHasLatex,
|
|
96800
|
+
title: dependencyValues.title,
|
|
96801
|
+
showLegend: dependencyValues.showLegend,
|
|
96802
|
+
legendPosition: dependencyValues.legendPosition,
|
|
96803
|
+
displayValues: dependencyValues.displayValues,
|
|
96804
|
+
shortDescription: dependencyValues.shortDescription,
|
|
96805
|
+
darkMode
|
|
96806
|
+
});
|
|
96807
|
+
return {
|
|
96808
|
+
setValue: { prefigureXML: xml2 },
|
|
96809
|
+
sendDiagnostics: diagnostics3
|
|
96810
|
+
};
|
|
96811
|
+
}
|
|
96196
96812
|
const shared = {
|
|
96197
96813
|
geometry: dependencyValues.chartGeometry,
|
|
96198
96814
|
seriesRendering,
|
|
@@ -96737,7 +97353,7 @@ class Series extends BaseComponent {
|
|
|
96737
97353
|
};
|
|
96738
97354
|
attributes.x = {
|
|
96739
97355
|
createComponentOfType: "numberList",
|
|
96740
|
-
description: "The horizontal coordinate of each value, for a chart whose marks are placed by measurement. Without it a
|
|
97356
|
+
description: "The horizontal coordinate of each value, for a chart whose marks are placed by measurement. Without it a line or scatter chart draws its marks under `categories`, at 1, 2, 3 and so on. A box plot and a histogram read a series as raw observations and take their positions from the data itself, so neither reads it.",
|
|
96741
97357
|
highlighted: true
|
|
96742
97358
|
};
|
|
96743
97359
|
return attributes;
|
|
@@ -243445,6 +244061,17 @@ const correctAttributeCapitalization = () => {
|
|
|
243445
244061
|
});
|
|
243446
244062
|
};
|
|
243447
244063
|
};
|
|
244064
|
+
const LEGACY_V06_ELEMENT_NAMES = [
|
|
244065
|
+
"copy",
|
|
244066
|
+
"map",
|
|
244067
|
+
"template",
|
|
244068
|
+
"sources",
|
|
244069
|
+
"extract",
|
|
244070
|
+
"customAttribute",
|
|
244071
|
+
"constraints",
|
|
244072
|
+
"styleDefinitions",
|
|
244073
|
+
"feedbackDefinitions"
|
|
244074
|
+
];
|
|
243448
244075
|
const correctElementCapitalization = () => {
|
|
243449
244076
|
const componentInfoObjects2 = createComponentInfoObjects();
|
|
243450
244077
|
const correctCapitalization = Object.keys(
|
|
@@ -243453,6 +244080,9 @@ const correctElementCapitalization = () => {
|
|
|
243453
244080
|
const correctCapitalizationMap = Object.fromEntries(
|
|
243454
244081
|
correctCapitalization.map((name2) => [name2.toLowerCase(), name2])
|
|
243455
244082
|
);
|
|
244083
|
+
for (const name2 of LEGACY_V06_ELEMENT_NAMES) {
|
|
244084
|
+
correctCapitalizationMap[name2.toLowerCase()] ??= name2;
|
|
244085
|
+
}
|
|
243456
244086
|
return (tree) => {
|
|
243457
244087
|
visit(tree, (node) => {
|
|
243458
244088
|
if (!isDastElement(node)) {
|
|
@@ -243513,34 +244143,6 @@ function renameAttrInPlace(elm, oldName, newName) {
|
|
|
243513
244143
|
);
|
|
243514
244144
|
elm.attributes = newAttrs;
|
|
243515
244145
|
}
|
|
243516
|
-
function getUniqueName(tree, baseName) {
|
|
243517
|
-
const allNames = /* @__PURE__ */ new Set();
|
|
243518
|
-
visit(tree, (node) => {
|
|
243519
|
-
if (!isDastElement(node)) {
|
|
243520
|
-
return;
|
|
243521
|
-
}
|
|
243522
|
-
if (node.attributes["name"]) {
|
|
243523
|
-
allNames.add(toXml(node.attributes["name"].children).trim());
|
|
243524
|
-
}
|
|
243525
|
-
if (node.attributes["assignNames"]) {
|
|
243526
|
-
const assignNames = toXml(
|
|
243527
|
-
node.attributes["assignNames"].children
|
|
243528
|
-
).trim();
|
|
243529
|
-
if (assignNames) {
|
|
243530
|
-
for (const name22 of assignNames.split(/\s+/)) {
|
|
243531
|
-
allNames.add(name22);
|
|
243532
|
-
}
|
|
243533
|
-
}
|
|
243534
|
-
}
|
|
243535
|
-
});
|
|
243536
|
-
let name2 = baseName;
|
|
243537
|
-
let i2 = 1;
|
|
243538
|
-
while (allNames.has(name2)) {
|
|
243539
|
-
name2 = `${baseName}${i2}`;
|
|
243540
|
-
i2++;
|
|
243541
|
-
}
|
|
243542
|
-
return name2;
|
|
243543
|
-
}
|
|
243544
244146
|
const componentInfoObjects = createComponentInfoObjects();
|
|
243545
244147
|
function determinePropType(componentType, propName, nIndices = 0) {
|
|
243546
244148
|
const publicStateVariableInfo = componentInfoObjects.publicStateVariableInfo[componentType];
|
|
@@ -243623,10 +244225,889 @@ function componentTypeFromWrapping(wrappingComponents, nDimensions, nIndices) {
|
|
|
243623
244225
|
}
|
|
243624
244226
|
return { foundComponentType: false };
|
|
243625
244227
|
}
|
|
243626
|
-
|
|
244228
|
+
function isModuleComponentType(componentType) {
|
|
244229
|
+
return componentInfoObjects.isInheritedComponentType({
|
|
244230
|
+
inheritedComponentType: componentType,
|
|
244231
|
+
baseComponentType: "module"
|
|
244232
|
+
});
|
|
244233
|
+
}
|
|
244234
|
+
function breakStringInPiecesBySpacesOrParens(string) {
|
|
244235
|
+
if (typeof string !== "string") {
|
|
244236
|
+
return { success: false };
|
|
244237
|
+
}
|
|
244238
|
+
let numParens = 0;
|
|
244239
|
+
const pieces = [];
|
|
244240
|
+
string = string.trim();
|
|
244241
|
+
let beginInd = 0;
|
|
244242
|
+
for (let ind = 0; ind < string.length; ind++) {
|
|
244243
|
+
const char = string[ind];
|
|
244244
|
+
if (char === "(") {
|
|
244245
|
+
if (numParens === 0) {
|
|
244246
|
+
const newPiece2 = string.substring(beginInd, ind).trim();
|
|
244247
|
+
if (newPiece2.length > 0) {
|
|
244248
|
+
pieces.push(newPiece2);
|
|
244249
|
+
}
|
|
244250
|
+
beginInd = ind;
|
|
244251
|
+
}
|
|
244252
|
+
numParens++;
|
|
244253
|
+
} else if (char === ")") {
|
|
244254
|
+
if (numParens === 0) {
|
|
244255
|
+
return { success: false };
|
|
244256
|
+
}
|
|
244257
|
+
if (numParens === 1) {
|
|
244258
|
+
const newPiece2 = string.substring(beginInd + 1, ind).trim();
|
|
244259
|
+
if (newPiece2.length > 0) {
|
|
244260
|
+
const result2 = breakStringInPiecesBySpacesOrParens(newPiece2);
|
|
244261
|
+
if (result2.success === true) {
|
|
244262
|
+
pieces.push(result2.pieces);
|
|
244263
|
+
} else {
|
|
244264
|
+
pieces.push(newPiece2);
|
|
244265
|
+
}
|
|
244266
|
+
}
|
|
244267
|
+
beginInd = ind + 1;
|
|
244268
|
+
}
|
|
244269
|
+
numParens--;
|
|
244270
|
+
} else if (numParens === 0 && char.match(/\s/)) {
|
|
244271
|
+
const newPiece2 = string.substring(beginInd, ind).trim();
|
|
244272
|
+
if (newPiece2.length > 0) {
|
|
244273
|
+
pieces.push(newPiece2);
|
|
244274
|
+
}
|
|
244275
|
+
beginInd = ind;
|
|
244276
|
+
}
|
|
244277
|
+
}
|
|
244278
|
+
if (numParens !== 0) {
|
|
244279
|
+
return { success: false };
|
|
244280
|
+
}
|
|
244281
|
+
const newPiece = string.substring(beginInd, string.length).trim();
|
|
244282
|
+
if (newPiece.length > 0) {
|
|
244283
|
+
pieces.push(newPiece);
|
|
244284
|
+
}
|
|
244285
|
+
return { success: true, pieces };
|
|
244286
|
+
}
|
|
244287
|
+
function firstLeafName(pieces) {
|
|
244288
|
+
for (const piece of pieces) {
|
|
244289
|
+
if (typeof piece === "string") {
|
|
244290
|
+
return piece;
|
|
244291
|
+
}
|
|
244292
|
+
const nested = firstLeafName(piece);
|
|
244293
|
+
if (nested !== void 0) {
|
|
244294
|
+
return nested;
|
|
244295
|
+
}
|
|
244296
|
+
}
|
|
244297
|
+
return void 0;
|
|
244298
|
+
}
|
|
244299
|
+
const VALID_REFERENCEABLE_NAME = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
244300
|
+
function isValidReferenceableName(name2) {
|
|
244301
|
+
return VALID_REFERENCEABLE_NAME.test(name2);
|
|
244302
|
+
}
|
|
244303
|
+
function makeIndexedPathPart(name2, indices) {
|
|
244304
|
+
return {
|
|
244305
|
+
type: "pathPart",
|
|
244306
|
+
name: name2,
|
|
244307
|
+
index: indices.map((i2) => ({
|
|
244308
|
+
type: "index",
|
|
244309
|
+
value: [{ type: "text", value: `${i2}` }]
|
|
244310
|
+
}))
|
|
244311
|
+
};
|
|
244312
|
+
}
|
|
244313
|
+
class RenameRegistry {
|
|
244314
|
+
constructor(existingNames = /* @__PURE__ */ new Set()) {
|
|
244315
|
+
this._renames = /* @__PURE__ */ new Map();
|
|
244316
|
+
this._matched = /* @__PURE__ */ new Set();
|
|
244317
|
+
this._existingNames = existingNames;
|
|
244318
|
+
}
|
|
244319
|
+
/**
|
|
244320
|
+
* Register that `oldName` should be replaced by `replacement` wherever it appears in
|
|
244321
|
+
* a macro path. The first registration of a name wins; a later one warns.
|
|
244322
|
+
*
|
|
244323
|
+
* Pass `undefined` for `replacement` to claim the name without rewriting anything,
|
|
244324
|
+
* which is what a composite that simply took the assigned name as its own `name`
|
|
244325
|
+
* needs: references already resolve, but a later composite assigning the same name
|
|
244326
|
+
* must not silently redirect them.
|
|
244327
|
+
*/
|
|
244328
|
+
register(oldName, replacement, origin, file) {
|
|
244329
|
+
if (!oldName) {
|
|
244330
|
+
return;
|
|
244331
|
+
}
|
|
244332
|
+
const existing = this._renames.get(oldName) ?? [];
|
|
244333
|
+
const scope = origin.ancestorNames ?? [];
|
|
244334
|
+
const sameScope = existing.find(
|
|
244335
|
+
(target) => sameScopeAs(target.origin.ancestorNames ?? [], scope)
|
|
244336
|
+
);
|
|
244337
|
+
if (sameScope) {
|
|
244338
|
+
file.message(
|
|
244339
|
+
`The name "${oldName}" is assigned by both <${sameScope.origin.elementName}> and <${origin.elementName}> in the same place; references to it were converted as if they referred to the first.`,
|
|
244340
|
+
{
|
|
244341
|
+
place: origin.position,
|
|
244342
|
+
ruleId: "assign-names/duplicate-name",
|
|
244343
|
+
source: "v06-to-v07"
|
|
244344
|
+
}
|
|
244345
|
+
);
|
|
244346
|
+
return;
|
|
244347
|
+
}
|
|
244348
|
+
if (existing.length === 0 && this._existingNames.has(oldName)) {
|
|
244349
|
+
file.message(
|
|
244350
|
+
`The name "${oldName}" assigned by <${origin.elementName}> is also used as the "name" of another component; all references to "${oldName}" were converted to point at the <${origin.elementName}> replacement.`,
|
|
244351
|
+
{
|
|
244352
|
+
place: origin.position,
|
|
244353
|
+
ruleId: "assign-names/shadows-existing-name",
|
|
244354
|
+
source: "v06-to-v07"
|
|
244355
|
+
}
|
|
244356
|
+
);
|
|
244357
|
+
}
|
|
244358
|
+
existing.push({ replacement, origin });
|
|
244359
|
+
this._renames.set(oldName, existing);
|
|
244360
|
+
}
|
|
244361
|
+
/**
|
|
244362
|
+
* The rename for `name`, chosen by which of them the reference is reaching into.
|
|
244363
|
+
*
|
|
244364
|
+
* `precedingNames` are the path parts written before it, so `$g2.a` looks up `a` with
|
|
244365
|
+
* `["g2"]`. The registration whose enclosing names are the longest match at the end of
|
|
244366
|
+
* that wins; one recorded with no enclosing names matches anything, which is what
|
|
244367
|
+
* keeps an unscoped document behaving exactly as before.
|
|
244368
|
+
*/
|
|
244369
|
+
get(name2, precedingNames = [], writtenOn) {
|
|
244370
|
+
const targets = this._renames.get(name2);
|
|
244371
|
+
if (!targets || targets.length === 0) {
|
|
244372
|
+
return void 0;
|
|
244373
|
+
}
|
|
244374
|
+
let best;
|
|
244375
|
+
let bestScore = -1;
|
|
244376
|
+
for (const target of targets) {
|
|
244377
|
+
if (writtenOn && target.origin.element === writtenOn) {
|
|
244378
|
+
continue;
|
|
244379
|
+
}
|
|
244380
|
+
const scope = target.origin.ancestorNames ?? [];
|
|
244381
|
+
if (!isSuffixOf(scope, precedingNames)) {
|
|
244382
|
+
continue;
|
|
244383
|
+
}
|
|
244384
|
+
if (scope.length > bestScore) {
|
|
244385
|
+
best = target;
|
|
244386
|
+
bestScore = scope.length;
|
|
244387
|
+
}
|
|
244388
|
+
}
|
|
244389
|
+
if (best) {
|
|
244390
|
+
this._matched.add(name2);
|
|
244391
|
+
}
|
|
244392
|
+
return best;
|
|
244393
|
+
}
|
|
244394
|
+
/** Whether `name` is registered *and* some registration rewrites references to it. */
|
|
244395
|
+
hasReplacement(name2) {
|
|
244396
|
+
return (this._renames.get(name2) ?? []).some(
|
|
244397
|
+
(target) => target.replacement !== void 0
|
|
244398
|
+
);
|
|
244399
|
+
}
|
|
244400
|
+
get size() {
|
|
244401
|
+
return this._renames.size;
|
|
244402
|
+
}
|
|
244403
|
+
/** Registered names that were never referenced anywhere in the document. */
|
|
244404
|
+
unused() {
|
|
244405
|
+
return [...this._renames.keys()].filter((n2) => !this._matched.has(n2));
|
|
244406
|
+
}
|
|
244407
|
+
}
|
|
244408
|
+
function sameScopeAs(a2, b2) {
|
|
244409
|
+
return a2.length === b2.length && a2.every((name2, i2) => name2 === b2[i2]);
|
|
244410
|
+
}
|
|
244411
|
+
function isSuffixOf(scope, path) {
|
|
244412
|
+
if (scope.length > path.length) {
|
|
244413
|
+
return false;
|
|
244414
|
+
}
|
|
244415
|
+
const offset = path.length - scope.length;
|
|
244416
|
+
return scope.every((name2, i2) => name2 === path[offset + i2]);
|
|
244417
|
+
}
|
|
244418
|
+
function collectExistingNames(tree) {
|
|
244419
|
+
const names = /* @__PURE__ */ new Set();
|
|
244420
|
+
visit(tree, (node) => {
|
|
244421
|
+
if (!isDastElement(node)) {
|
|
244422
|
+
return;
|
|
244423
|
+
}
|
|
244424
|
+
const nameAttr = Object.entries(node.attributes).find(
|
|
244425
|
+
([name2]) => name2.toLowerCase() === "name"
|
|
244426
|
+
)?.[1];
|
|
244427
|
+
if (nameAttr) {
|
|
244428
|
+
const name2 = toXml(nameAttr.children).trim();
|
|
244429
|
+
if (name2) {
|
|
244430
|
+
names.add(name2);
|
|
244431
|
+
}
|
|
244432
|
+
}
|
|
244433
|
+
});
|
|
244434
|
+
return names;
|
|
244435
|
+
}
|
|
244436
|
+
function collectAllNames(tree) {
|
|
244437
|
+
const names = collectExistingNames(tree);
|
|
244438
|
+
visit(tree, (node) => {
|
|
244439
|
+
if (!isDastElement(node)) {
|
|
244440
|
+
return;
|
|
244441
|
+
}
|
|
244442
|
+
const assignNamesAttr = Object.entries(node.attributes).find(
|
|
244443
|
+
([name2]) => name2.toLowerCase() === "assignnames"
|
|
244444
|
+
)?.[1];
|
|
244445
|
+
if (!assignNamesAttr) {
|
|
244446
|
+
return;
|
|
244447
|
+
}
|
|
244448
|
+
const assignNames = toXml(assignNamesAttr.children).trim();
|
|
244449
|
+
for (const name2 of assignNames.split(/[\s()]+/)) {
|
|
244450
|
+
if (name2) {
|
|
244451
|
+
names.add(name2);
|
|
244452
|
+
}
|
|
244453
|
+
}
|
|
244454
|
+
});
|
|
244455
|
+
return names;
|
|
244456
|
+
}
|
|
244457
|
+
function createUniqueNameFactory(tree) {
|
|
244458
|
+
const used = collectAllNames(tree);
|
|
244459
|
+
return (baseName) => {
|
|
244460
|
+
let name2 = baseName;
|
|
244461
|
+
let i2 = 1;
|
|
244462
|
+
while (used.has(name2)) {
|
|
244463
|
+
name2 = `${baseName}${i2}`;
|
|
244464
|
+
i2++;
|
|
244465
|
+
}
|
|
244466
|
+
used.add(name2);
|
|
244467
|
+
return name2;
|
|
244468
|
+
};
|
|
244469
|
+
}
|
|
244470
|
+
function isV06True(attr) {
|
|
244471
|
+
if (!attr) {
|
|
244472
|
+
return false;
|
|
244473
|
+
}
|
|
244474
|
+
const value = toXml(attr.children).trim().toLowerCase();
|
|
244475
|
+
return value === "" || value === "true";
|
|
244476
|
+
}
|
|
244477
|
+
const COMPOSITES = {
|
|
244478
|
+
select: {
|
|
244479
|
+
name: "select",
|
|
244480
|
+
count: { kind: "attr", attr: "numToSelect" },
|
|
244481
|
+
grouping: { childName: "option" }
|
|
244482
|
+
},
|
|
244483
|
+
selectfromsequence: {
|
|
244484
|
+
name: "selectFromSequence",
|
|
244485
|
+
count: { kind: "attr", attr: "numToSelect" }
|
|
244486
|
+
},
|
|
244487
|
+
selectrandomnumbers: {
|
|
244488
|
+
name: "selectRandomNumbers",
|
|
244489
|
+
count: { kind: "attr", attr: "numToSelect" }
|
|
244490
|
+
},
|
|
244491
|
+
samplerandomnumbers: {
|
|
244492
|
+
name: "sampleRandomNumbers",
|
|
244493
|
+
count: { kind: "attr", attr: "numSamples" }
|
|
244494
|
+
},
|
|
244495
|
+
selectprimenumbers: {
|
|
244496
|
+
name: "selectPrimeNumbers",
|
|
244497
|
+
count: { kind: "attr", attr: "numToSelect" }
|
|
244498
|
+
},
|
|
244499
|
+
sampleprimenumbers: {
|
|
244500
|
+
name: "samplePrimeNumbers",
|
|
244501
|
+
count: { kind: "attr", attr: "numSamples" }
|
|
244502
|
+
},
|
|
244503
|
+
conditionalcontent: {
|
|
244504
|
+
name: "conditionalContent",
|
|
244505
|
+
count: { kind: "always-one" },
|
|
244506
|
+
grouping: { childName: "case", alsoMatch: ["else"] }
|
|
244507
|
+
},
|
|
244508
|
+
lorem: { name: "lorem", count: { kind: "unknown" } },
|
|
244509
|
+
sort: { name: "sort", count: { kind: "unknown" } },
|
|
244510
|
+
shuffle: { name: "shuffle", count: { kind: "unknown" } },
|
|
244511
|
+
group: { name: "group", count: { kind: "unknown" } },
|
|
244512
|
+
sequence: { name: "sequence", count: { kind: "unknown" } },
|
|
244513
|
+
substitute: { name: "substitute", count: { kind: "unknown" } },
|
|
244514
|
+
split: { name: "split", count: { kind: "unknown" } },
|
|
244515
|
+
intersection: { name: "intersection", count: { kind: "unknown" } },
|
|
244516
|
+
extract: {
|
|
244517
|
+
name: "extract",
|
|
244518
|
+
count: { kind: "unknown" },
|
|
244519
|
+
noV07Equivalent: true
|
|
244520
|
+
}
|
|
244521
|
+
};
|
|
244522
|
+
function lookupComposite(node) {
|
|
244523
|
+
return COMPOSITES[node.name.toLowerCase()];
|
|
244524
|
+
}
|
|
244525
|
+
function producesSingleReplacement(node, spec) {
|
|
244526
|
+
if (spec.count.kind === "always-one") {
|
|
244527
|
+
return true;
|
|
244528
|
+
}
|
|
244529
|
+
if (spec.count.kind === "unknown") {
|
|
244530
|
+
return false;
|
|
244531
|
+
}
|
|
244532
|
+
const attr = findAttribute(node, spec.count.attr);
|
|
244533
|
+
if (!attr) {
|
|
244534
|
+
return true;
|
|
244535
|
+
}
|
|
244536
|
+
const raw = toXml(attr.children).trim();
|
|
244537
|
+
return raw === "" || raw === "1";
|
|
244538
|
+
}
|
|
244539
|
+
function findAttribute(node, attrName) {
|
|
244540
|
+
return Object.entries(node.attributes).find(
|
|
244541
|
+
([name2]) => name2.toLowerCase() === attrName.toLowerCase()
|
|
244542
|
+
)?.[1];
|
|
244543
|
+
}
|
|
244544
|
+
function makePositionMap(node, spec, file) {
|
|
244545
|
+
if (!spec.grouping) {
|
|
244546
|
+
return void 0;
|
|
244547
|
+
}
|
|
244548
|
+
const names = [
|
|
244549
|
+
spec.grouping.childName,
|
|
244550
|
+
...spec.grouping.alsoMatch || []
|
|
244551
|
+
].map((n2) => n2.toLowerCase());
|
|
244552
|
+
const branches = node.children.filter(
|
|
244553
|
+
(child) => isDastElement(child) && names.includes(child.name.toLowerCase())
|
|
244554
|
+
);
|
|
244555
|
+
if (branches.length === 0) {
|
|
244556
|
+
return void 0;
|
|
244557
|
+
}
|
|
244558
|
+
return positionMapForBranches(
|
|
244559
|
+
branches.map((branch) => branch.children),
|
|
244560
|
+
node,
|
|
244561
|
+
file
|
|
244562
|
+
);
|
|
244563
|
+
}
|
|
244564
|
+
function makeTemplatePositionMap(template2, node, file) {
|
|
244565
|
+
return positionMapForBranches([template2.children], node, file);
|
|
244566
|
+
}
|
|
244567
|
+
function positionMapForBranches(branches, node, file) {
|
|
244568
|
+
const maps = branches.map((children) => branchPositionMap(children));
|
|
244569
|
+
const first2 = maps[0];
|
|
244570
|
+
if (maps.every((m2) => isIdentity(m2))) {
|
|
244571
|
+
return void 0;
|
|
244572
|
+
}
|
|
244573
|
+
if (!maps.every((m2) => sameMap(m2, first2))) {
|
|
244574
|
+
file.message(
|
|
244575
|
+
`The branches of <${node.name}> mix plain text with components differently, so the positions that assignNames referred to cannot be determined; the first branch was used.`,
|
|
244576
|
+
{
|
|
244577
|
+
place: node.position,
|
|
244578
|
+
ruleId: "assign-names/branch-index-skew",
|
|
244579
|
+
source: "v06-to-v07"
|
|
244580
|
+
}
|
|
244581
|
+
);
|
|
244582
|
+
}
|
|
244583
|
+
file.message(
|
|
244584
|
+
`<${node.name}> has a branch that mixes plain text with components. v0.6 skipped the text when assigning names but v0.7 counts it, so the converted indices are a best guess and should be checked.`,
|
|
244585
|
+
{
|
|
244586
|
+
place: node.position,
|
|
244587
|
+
ruleId: "assign-names/primitive-skew",
|
|
244588
|
+
source: "v06-to-v07"
|
|
244589
|
+
}
|
|
244590
|
+
);
|
|
244591
|
+
const insideGroupingDepth = 2;
|
|
244592
|
+
return (depth, ordinal) => {
|
|
244593
|
+
if (depth !== insideGroupingDepth) {
|
|
244594
|
+
return ordinal;
|
|
244595
|
+
}
|
|
244596
|
+
return first2[ordinal - 1];
|
|
244597
|
+
};
|
|
244598
|
+
}
|
|
244599
|
+
function branchPositionMap(children) {
|
|
244600
|
+
const result2 = [];
|
|
244601
|
+
let position2 = 0;
|
|
244602
|
+
for (const child of children) {
|
|
244603
|
+
if (child.type === "text") {
|
|
244604
|
+
if (child.value.trim() === "") {
|
|
244605
|
+
continue;
|
|
244606
|
+
}
|
|
244607
|
+
position2++;
|
|
244608
|
+
continue;
|
|
244609
|
+
}
|
|
244610
|
+
if (child.type === "comment" || child.type === "instruction") {
|
|
244611
|
+
continue;
|
|
244612
|
+
}
|
|
244613
|
+
position2++;
|
|
244614
|
+
result2.push(position2);
|
|
244615
|
+
}
|
|
244616
|
+
return result2;
|
|
244617
|
+
}
|
|
244618
|
+
function isIdentity(map2) {
|
|
244619
|
+
return map2.every((v2, i2) => v2 === i2 + 1);
|
|
244620
|
+
}
|
|
244621
|
+
function sameMap(a2, b2) {
|
|
244622
|
+
return a2.length === b2.length && a2.every((v2, i2) => v2 === b2[i2]);
|
|
244623
|
+
}
|
|
244624
|
+
function createAssignNamesContext(tree) {
|
|
244625
|
+
const existingNames = collectExistingNames(tree);
|
|
244626
|
+
const namespaceElements = /* @__PURE__ */ new WeakMap();
|
|
244627
|
+
let unnamedCount = 0;
|
|
244628
|
+
visit(tree, (node) => {
|
|
244629
|
+
if (!isDastElement(node)) {
|
|
244630
|
+
return;
|
|
244631
|
+
}
|
|
244632
|
+
const newNamespaceKey = Object.keys(node.attributes).find(
|
|
244633
|
+
(key) => key.toLowerCase() === "newnamespace"
|
|
244634
|
+
);
|
|
244635
|
+
if (newNamespaceKey === void 0) {
|
|
244636
|
+
return;
|
|
244637
|
+
}
|
|
244638
|
+
const newNamespaceValue = toXml(
|
|
244639
|
+
node.attributes[newNamespaceKey].children
|
|
244640
|
+
).trim().toLowerCase();
|
|
244641
|
+
if (newNamespaceValue !== "" && newNamespaceValue !== "true") {
|
|
244642
|
+
return;
|
|
244643
|
+
}
|
|
244644
|
+
const nameAttr = findAttribute(node, "name");
|
|
244645
|
+
const name2 = nameAttr ? toXml(nameAttr.children).trim() : "";
|
|
244646
|
+
namespaceElements.set(node, name2 || `\0ns${++unnamedCount}`);
|
|
244647
|
+
});
|
|
244648
|
+
const context = {
|
|
244649
|
+
registry: new RenameRegistry(existingNames),
|
|
244650
|
+
uniqueName: createUniqueNameFactory(tree),
|
|
244651
|
+
existingNames,
|
|
244652
|
+
claimedNames: /* @__PURE__ */ new Set(),
|
|
244653
|
+
namespaceElements,
|
|
244654
|
+
existingNameScopes: /* @__PURE__ */ new Map()
|
|
244655
|
+
};
|
|
244656
|
+
visit(tree, (node, info) => {
|
|
244657
|
+
if (!isDastElement(node)) {
|
|
244658
|
+
return;
|
|
244659
|
+
}
|
|
244660
|
+
const nameAttr = findAttribute(node, "name");
|
|
244661
|
+
const name2 = nameAttr ? toXml(nameAttr.children).trim() : "";
|
|
244662
|
+
if (!name2) {
|
|
244663
|
+
return;
|
|
244664
|
+
}
|
|
244665
|
+
const scopes = context.existingNameScopes.get(name2) ?? [];
|
|
244666
|
+
scopes.push(namespaceChainOf(info.parents, context));
|
|
244667
|
+
context.existingNameScopes.set(name2, scopes);
|
|
244668
|
+
});
|
|
244669
|
+
return context;
|
|
244670
|
+
}
|
|
244671
|
+
function nameTakenInScope(context, name2, ancestorNames) {
|
|
244672
|
+
const scopes = context.existingNameScopes.get(name2);
|
|
244673
|
+
if (!scopes) {
|
|
244674
|
+
return false;
|
|
244675
|
+
}
|
|
244676
|
+
const here = ancestorNames ?? [];
|
|
244677
|
+
return scopes.some(
|
|
244678
|
+
(scope) => scope.length === here.length && scope.every((segment, i2) => segment === here[i2])
|
|
244679
|
+
);
|
|
244680
|
+
}
|
|
244681
|
+
function chooseCompositeName(node, pieces, fallbackBase, context, file) {
|
|
244682
|
+
const nameAttr = findAttribute(node, "name");
|
|
244683
|
+
const existingName = nameAttr ? toXml(nameAttr.children).trim() : "";
|
|
244684
|
+
if (existingName && isValidReferenceableName(existingName)) {
|
|
244685
|
+
return existingName;
|
|
244686
|
+
}
|
|
244687
|
+
if (existingName) {
|
|
244688
|
+
file?.message(
|
|
244689
|
+
`<${node.name}> is called "${existingName}", which v0.7 cannot use as a name — it must start with a letter and hold only letters, digits, underscores and hyphens. A different name was used instead.`,
|
|
244690
|
+
{
|
|
244691
|
+
place: node.position,
|
|
244692
|
+
ruleId: "assign-names/invalid-name",
|
|
244693
|
+
source: "v06-to-v07"
|
|
244694
|
+
}
|
|
244695
|
+
);
|
|
244696
|
+
}
|
|
244697
|
+
const firstName = firstLeafName(pieces);
|
|
244698
|
+
if (firstName && isValidReferenceableName(firstName) && !context.existingNames.has(firstName) && !context.claimedNames.has(firstName)) {
|
|
244699
|
+
context.claimedNames.add(firstName);
|
|
244700
|
+
return firstName;
|
|
244701
|
+
}
|
|
244702
|
+
return context.uniqueName(fallbackBase);
|
|
244703
|
+
}
|
|
244704
|
+
function setCompositeName(node, name2) {
|
|
244705
|
+
const nameAttr = findAttribute(node, "name");
|
|
244706
|
+
const oldKey = assignNamesKey(node);
|
|
244707
|
+
if (nameAttr) {
|
|
244708
|
+
if (toXml(nameAttr.children) !== name2) {
|
|
244709
|
+
nameAttr.children = [{ type: "text", value: name2 }];
|
|
244710
|
+
}
|
|
244711
|
+
deleteAssignNames(node);
|
|
244712
|
+
return;
|
|
244713
|
+
}
|
|
244714
|
+
if (oldKey !== void 0) {
|
|
244715
|
+
renameAttrInPlace(node, oldKey, "name");
|
|
244716
|
+
node.attributes["name"].children = [{ type: "text", value: name2 }];
|
|
244717
|
+
return;
|
|
244718
|
+
}
|
|
244719
|
+
node.attributes["name"] = {
|
|
244720
|
+
type: "attribute",
|
|
244721
|
+
name: "name",
|
|
244722
|
+
children: [{ type: "text", value: name2 }]
|
|
244723
|
+
};
|
|
244724
|
+
}
|
|
244725
|
+
function readAssignNames(node) {
|
|
244726
|
+
const key = assignNamesKey(node);
|
|
244727
|
+
if (key === void 0) {
|
|
244728
|
+
return void 0;
|
|
244729
|
+
}
|
|
244730
|
+
return toXml(node.attributes[key].children).trim();
|
|
244731
|
+
}
|
|
244732
|
+
function deleteAssignNames(node) {
|
|
244733
|
+
const key = assignNamesKey(node);
|
|
244734
|
+
if (key !== void 0) {
|
|
244735
|
+
delete node.attributes[key];
|
|
244736
|
+
}
|
|
244737
|
+
}
|
|
244738
|
+
function assignNamesKey(node) {
|
|
244739
|
+
return Object.keys(node.attributes).find(
|
|
244740
|
+
(k2) => k2.toLowerCase() === "assignnames"
|
|
244741
|
+
);
|
|
244742
|
+
}
|
|
244743
|
+
function namespaceChainOf(parents, context) {
|
|
244744
|
+
const chain2 = [];
|
|
244745
|
+
for (const parent of parents) {
|
|
244746
|
+
const segment = context.namespaceElements.get(parent);
|
|
244747
|
+
if (segment !== void 0) {
|
|
244748
|
+
chain2.push(segment);
|
|
244749
|
+
}
|
|
244750
|
+
}
|
|
244751
|
+
return chain2.reverse();
|
|
244752
|
+
}
|
|
244753
|
+
function inheritNamespace(from, to2, context) {
|
|
244754
|
+
const segment = context.namespaceElements.get(from);
|
|
244755
|
+
if (segment !== void 0) {
|
|
244756
|
+
context.namespaceElements.set(to2, segment);
|
|
244757
|
+
}
|
|
244758
|
+
}
|
|
244759
|
+
const identityPositionMap = (_depth, ordinal) => ordinal;
|
|
244760
|
+
function registerAssignNames({
|
|
244761
|
+
assignNamesValue,
|
|
244762
|
+
compositeName,
|
|
244763
|
+
registry,
|
|
244764
|
+
origin,
|
|
244765
|
+
file,
|
|
244766
|
+
positionMap = identityPositionMap
|
|
244767
|
+
}) {
|
|
244768
|
+
const result2 = breakStringInPiecesBySpacesOrParens(assignNamesValue);
|
|
244769
|
+
if (!result2.success) {
|
|
244770
|
+
file.message(
|
|
244771
|
+
`Could not parse assignNames="${assignNamesValue}" on <${origin.elementName}> because its parentheses are unbalanced; references to those names were not converted.`,
|
|
244772
|
+
{
|
|
244773
|
+
place: origin.position,
|
|
244774
|
+
ruleId: "assign-names/unbalanced-parens",
|
|
244775
|
+
source: "v06-to-v07"
|
|
244776
|
+
}
|
|
244777
|
+
);
|
|
244778
|
+
return false;
|
|
244779
|
+
}
|
|
244780
|
+
registerPieces(result2.pieces, []);
|
|
244781
|
+
return true;
|
|
244782
|
+
function registerPieces(pieces, indexPath) {
|
|
244783
|
+
pieces.forEach((piece, i2) => {
|
|
244784
|
+
const depth = indexPath.length + 1;
|
|
244785
|
+
const position2 = positionMap(depth, i2 + 1);
|
|
244786
|
+
if (position2 === void 0) {
|
|
244787
|
+
reportNamesNothing(piece);
|
|
244788
|
+
return;
|
|
244789
|
+
}
|
|
244790
|
+
const indices = [...indexPath, position2];
|
|
244791
|
+
if (typeof piece === "string") {
|
|
244792
|
+
if (!isValidReferenceableName(piece)) {
|
|
244793
|
+
file.message(
|
|
244794
|
+
`The assignNames token "${piece}" on <${origin.elementName}> is not a valid v0.7 name, so references to it were not converted.`,
|
|
244795
|
+
{
|
|
244796
|
+
place: origin.position,
|
|
244797
|
+
ruleId: "assign-names/invalid-name",
|
|
244798
|
+
source: "v06-to-v07"
|
|
244799
|
+
}
|
|
244800
|
+
);
|
|
244801
|
+
return;
|
|
244802
|
+
}
|
|
244803
|
+
registry.register(
|
|
244804
|
+
piece,
|
|
244805
|
+
[makeIndexedPathPart(compositeName, indices)],
|
|
244806
|
+
origin,
|
|
244807
|
+
file
|
|
244808
|
+
);
|
|
244809
|
+
} else {
|
|
244810
|
+
registerPieces(piece, indices);
|
|
244811
|
+
}
|
|
244812
|
+
});
|
|
244813
|
+
}
|
|
244814
|
+
function reportNamesNothing(piece) {
|
|
244815
|
+
if (typeof piece !== "string") {
|
|
244816
|
+
piece.forEach(reportNamesNothing);
|
|
244817
|
+
return;
|
|
244818
|
+
}
|
|
244819
|
+
file.message(
|
|
244820
|
+
`The assignNames token "${piece}" on <${origin.elementName}> named nothing in v0.6 — the position it was given holds no component — so references to it were not converted.`,
|
|
244821
|
+
{
|
|
244822
|
+
place: origin.position,
|
|
244823
|
+
ruleId: "assign-names/names-nothing",
|
|
244824
|
+
source: "v06-to-v07"
|
|
244825
|
+
}
|
|
244826
|
+
);
|
|
244827
|
+
}
|
|
244828
|
+
}
|
|
244829
|
+
function registerCompositeAssignNames({
|
|
244830
|
+
node,
|
|
244831
|
+
assignNamesValue,
|
|
244832
|
+
fallbackBase,
|
|
244833
|
+
ancestorNames,
|
|
244834
|
+
context,
|
|
244835
|
+
file,
|
|
244836
|
+
positionMap
|
|
244837
|
+
}) {
|
|
244838
|
+
const origin = {
|
|
244839
|
+
elementName: node.name,
|
|
244840
|
+
position: node.position,
|
|
244841
|
+
ancestorNames,
|
|
244842
|
+
element: node
|
|
244843
|
+
};
|
|
244844
|
+
const parsed = breakStringInPiecesBySpacesOrParens(assignNamesValue);
|
|
244845
|
+
if (!parsed.success) {
|
|
244846
|
+
registerAssignNames({
|
|
244847
|
+
assignNamesValue,
|
|
244848
|
+
compositeName: "",
|
|
244849
|
+
registry: context.registry,
|
|
244850
|
+
origin,
|
|
244851
|
+
file
|
|
244852
|
+
});
|
|
244853
|
+
return void 0;
|
|
244854
|
+
}
|
|
244855
|
+
if (firstLeafName(parsed.pieces) === void 0) {
|
|
244856
|
+
return void 0;
|
|
244857
|
+
}
|
|
244858
|
+
const compositeName = chooseCompositeName(
|
|
244859
|
+
node,
|
|
244860
|
+
parsed.pieces,
|
|
244861
|
+
fallbackBase,
|
|
244862
|
+
context,
|
|
244863
|
+
file
|
|
244864
|
+
);
|
|
244865
|
+
registerAssignNames({
|
|
244866
|
+
assignNamesValue,
|
|
244867
|
+
compositeName,
|
|
244868
|
+
registry: context.registry,
|
|
244869
|
+
origin,
|
|
244870
|
+
file,
|
|
244871
|
+
positionMap
|
|
244872
|
+
});
|
|
244873
|
+
return compositeName;
|
|
244874
|
+
}
|
|
244875
|
+
function visitAll(tree, visitor) {
|
|
244876
|
+
walk(tree, [], void 0);
|
|
244877
|
+
function walk(node, parents, attributeOwner) {
|
|
244878
|
+
const isMacro = node.type === "macro" || node.type === "function";
|
|
244879
|
+
if (!isMacro) {
|
|
244880
|
+
visitor(node, parents, attributeOwner);
|
|
244881
|
+
}
|
|
244882
|
+
const innerParents = isDastElement(node) ? [node, ...parents] : parents;
|
|
244883
|
+
if (isDastElement(node)) {
|
|
244884
|
+
for (const attr of Object.values(node.attributes)) {
|
|
244885
|
+
walkArray(attr.children, innerParents, node);
|
|
244886
|
+
}
|
|
244887
|
+
}
|
|
244888
|
+
if (node.type === "macro") {
|
|
244889
|
+
walkMacroAttributes(node, innerParents, attributeOwner);
|
|
244890
|
+
walkMacroPath(node, innerParents, attributeOwner);
|
|
244891
|
+
const accessedProp = node.accessedProp;
|
|
244892
|
+
if (accessedProp) {
|
|
244893
|
+
walk(accessedProp, innerParents, attributeOwner);
|
|
244894
|
+
}
|
|
244895
|
+
}
|
|
244896
|
+
if (node.type === "function") {
|
|
244897
|
+
const inner = node.macro;
|
|
244898
|
+
if (inner) {
|
|
244899
|
+
walk(inner, innerParents, attributeOwner);
|
|
244900
|
+
}
|
|
244901
|
+
walkMacroPath(node, innerParents, attributeOwner);
|
|
244902
|
+
for (const inputPart of node.input || []) {
|
|
244903
|
+
walkArray(
|
|
244904
|
+
inputPart,
|
|
244905
|
+
innerParents,
|
|
244906
|
+
attributeOwner
|
|
244907
|
+
);
|
|
244908
|
+
}
|
|
244909
|
+
}
|
|
244910
|
+
if ("children" in node && Array.isArray(node.children)) {
|
|
244911
|
+
walkArray(
|
|
244912
|
+
node.children,
|
|
244913
|
+
innerParents,
|
|
244914
|
+
isDastElement(node) ? void 0 : attributeOwner
|
|
244915
|
+
);
|
|
244916
|
+
}
|
|
244917
|
+
if (isMacro) {
|
|
244918
|
+
visitor(node, parents, attributeOwner);
|
|
244919
|
+
}
|
|
244920
|
+
}
|
|
244921
|
+
function walkMacroAttributes(node, parents, attributeOwner) {
|
|
244922
|
+
const attrs = Array.isArray(node.attributes) ? node.attributes : Object.values(node.attributes);
|
|
244923
|
+
for (const attr of attrs) {
|
|
244924
|
+
walkArray(attr.children, parents, attributeOwner);
|
|
244925
|
+
}
|
|
244926
|
+
}
|
|
244927
|
+
function walkMacroPath(node, parents, attributeOwner) {
|
|
244928
|
+
for (const part of node.path || []) {
|
|
244929
|
+
for (const index2 of part.index) {
|
|
244930
|
+
walkArray(index2.value, parents, attributeOwner);
|
|
244931
|
+
}
|
|
244932
|
+
}
|
|
244933
|
+
}
|
|
244934
|
+
function walkArray(nodes, parents, attributeOwner) {
|
|
244935
|
+
for (let i2 = 0; i2 < nodes.length; i2++) {
|
|
244936
|
+
walk(nodes[i2], parents, attributeOwner);
|
|
244937
|
+
}
|
|
244938
|
+
}
|
|
244939
|
+
}
|
|
244940
|
+
function visitAllMacros(tree, visitor) {
|
|
244941
|
+
visitAll(tree, (node, parents, attributeOwner) => {
|
|
244942
|
+
if (node.type !== "macro" && node.type !== "function") {
|
|
244943
|
+
return;
|
|
244944
|
+
}
|
|
244945
|
+
if (!Array.isArray(node.path)) {
|
|
244946
|
+
return;
|
|
244947
|
+
}
|
|
244948
|
+
visitor(node, parents, attributeOwner);
|
|
244949
|
+
});
|
|
244950
|
+
}
|
|
244951
|
+
const propAccessParts = /* @__PURE__ */ new WeakSet();
|
|
244952
|
+
function markAsPropAccess(part) {
|
|
244953
|
+
propAccessParts.add(part);
|
|
244954
|
+
}
|
|
244955
|
+
function isPropAccess(part) {
|
|
244956
|
+
return propAccessParts.has(part);
|
|
244957
|
+
}
|
|
244958
|
+
const rawPropPositions = /* @__PURE__ */ new WeakMap();
|
|
244959
|
+
function markRawPropPositions(node, attrName, positions) {
|
|
244960
|
+
let byAttr = rawPropPositions.get(node);
|
|
244961
|
+
if (!byAttr) {
|
|
244962
|
+
byAttr = /* @__PURE__ */ new Map();
|
|
244963
|
+
rawPropPositions.set(node, byAttr);
|
|
244964
|
+
}
|
|
244965
|
+
byAttr.set(attrName.toLowerCase(), positions);
|
|
244966
|
+
}
|
|
244967
|
+
function restoreRawPropPositions(node, attrName, path) {
|
|
244968
|
+
const positions = rawPropPositions.get(node)?.get(attrName.toLowerCase());
|
|
244969
|
+
if (!positions) {
|
|
244970
|
+
return;
|
|
244971
|
+
}
|
|
244972
|
+
for (const position2 of positions) {
|
|
244973
|
+
const part = path[position2];
|
|
244974
|
+
if (part) {
|
|
244975
|
+
markAsPropAccess(part);
|
|
244976
|
+
}
|
|
244977
|
+
}
|
|
244978
|
+
}
|
|
244979
|
+
const RAW_REFERENCE_ATTRS = {
|
|
244980
|
+
copy: ["source"],
|
|
244981
|
+
extract: ["source"]
|
|
244982
|
+
};
|
|
244983
|
+
function applyRefRenames(tree, registry, file, context) {
|
|
244984
|
+
if (registry.size === 0) {
|
|
244985
|
+
return;
|
|
244986
|
+
}
|
|
244987
|
+
visitAllMacros(tree, (node, parents, attributeOwner) => {
|
|
244988
|
+
node.path = renamePath(
|
|
244989
|
+
node.path,
|
|
244990
|
+
registry,
|
|
244991
|
+
file,
|
|
244992
|
+
node.position,
|
|
244993
|
+
namespaceChainOf(parents, context),
|
|
244994
|
+
// A reference written on an element cannot mean the name that element is
|
|
244995
|
+
// about to take: `<copy source="$x0" assignNames="x0">` reaches elsewhere
|
|
244996
|
+
// for the thing it copies. References among its *children* are a different
|
|
244997
|
+
// matter and keep their ownership clear, so `visitAll` reports none for them.
|
|
244998
|
+
attributeOwner
|
|
244999
|
+
);
|
|
245000
|
+
});
|
|
245001
|
+
visitAll(tree, (node, parents) => {
|
|
245002
|
+
if (!isDastElement(node)) {
|
|
245003
|
+
return;
|
|
245004
|
+
}
|
|
245005
|
+
const attrNames = RAW_REFERENCE_ATTRS[node.name.toLowerCase()];
|
|
245006
|
+
if (!attrNames) {
|
|
245007
|
+
return;
|
|
245008
|
+
}
|
|
245009
|
+
const enclosing = namespaceChainOf(parents, context);
|
|
245010
|
+
for (const attrName of attrNames) {
|
|
245011
|
+
renameRawReferenceAttribute(
|
|
245012
|
+
node,
|
|
245013
|
+
attrName,
|
|
245014
|
+
registry,
|
|
245015
|
+
file,
|
|
245016
|
+
enclosing
|
|
245017
|
+
);
|
|
245018
|
+
}
|
|
245019
|
+
});
|
|
245020
|
+
}
|
|
245021
|
+
function renameRawReferenceAttribute(node, attrName, registry, file, enclosingNames) {
|
|
245022
|
+
const attr = Object.entries(node.attributes).find(
|
|
245023
|
+
([name2]) => name2.toLowerCase() === attrName.toLowerCase()
|
|
245024
|
+
)?.[1];
|
|
245025
|
+
if (!attr || attr.children.length !== 1) {
|
|
245026
|
+
return;
|
|
245027
|
+
}
|
|
245028
|
+
const child = attr.children[0];
|
|
245029
|
+
if (child.type !== "text") {
|
|
245030
|
+
return;
|
|
245031
|
+
}
|
|
245032
|
+
const value = child.value.trim();
|
|
245033
|
+
if (!value) {
|
|
245034
|
+
return;
|
|
245035
|
+
}
|
|
245036
|
+
if (!value.split(/[^A-Za-z0-9_-]+/).some((piece) => registry.hasReplacement(piece))) {
|
|
245037
|
+
return;
|
|
245038
|
+
}
|
|
245039
|
+
let path;
|
|
245040
|
+
try {
|
|
245041
|
+
path = parseReferencePath(value);
|
|
245042
|
+
} catch (e4) {
|
|
245043
|
+
file.message(
|
|
245044
|
+
`Could not convert a reference to an assignNames name in ${attrName}="${value}".`,
|
|
245045
|
+
{
|
|
245046
|
+
place: node.position,
|
|
245047
|
+
ruleId: "assign-names/unparsable-reference-attribute",
|
|
245048
|
+
source: "v06-to-v07"
|
|
245049
|
+
}
|
|
245050
|
+
);
|
|
245051
|
+
return;
|
|
245052
|
+
}
|
|
245053
|
+
restoreRawPropPositions(node, attrName, path);
|
|
245054
|
+
const asMacro = { type: "macro", path, attributes: {} };
|
|
245055
|
+
visitAllMacros(asMacro, (macro) => {
|
|
245056
|
+
macro.path = renamePath(
|
|
245057
|
+
macro.path,
|
|
245058
|
+
registry,
|
|
245059
|
+
file,
|
|
245060
|
+
node.position,
|
|
245061
|
+
enclosingNames,
|
|
245062
|
+
// A copy's `source` says what it copies, which is never the copy itself:
|
|
245063
|
+
// `<copy source="../x0" assignNames="x0">` names its result `x0` while
|
|
245064
|
+
// reaching out of the namespace for the `x0` it copies.
|
|
245065
|
+
node
|
|
245066
|
+
);
|
|
245067
|
+
});
|
|
245068
|
+
child.value = toXml(asMacro.path);
|
|
245069
|
+
}
|
|
245070
|
+
function renamePath(path, registry, file, place, enclosingNames = [], writtenOn) {
|
|
245071
|
+
if (!path.some((part) => registry.hasReplacement(part.name))) {
|
|
245072
|
+
return path;
|
|
245073
|
+
}
|
|
245074
|
+
return path.flatMap((part, partIndex) => {
|
|
245075
|
+
const target = registry.get(
|
|
245076
|
+
part.name,
|
|
245077
|
+
[...enclosingNames, ...path.slice(0, partIndex).map((p2) => p2.name)],
|
|
245078
|
+
writtenOn
|
|
245079
|
+
);
|
|
245080
|
+
if (!target?.replacement) {
|
|
245081
|
+
return [part];
|
|
245082
|
+
}
|
|
245083
|
+
if (partIndex > 0 && isPropAccess(part)) {
|
|
245084
|
+
return [part];
|
|
245085
|
+
}
|
|
245086
|
+
const replacement = structuredClone(
|
|
245087
|
+
target.replacement
|
|
245088
|
+
);
|
|
245089
|
+
const last2 = replacement[replacement.length - 1];
|
|
245090
|
+
last2.index = [...last2.index, ...part.index];
|
|
245091
|
+
return replacement;
|
|
245092
|
+
});
|
|
245093
|
+
}
|
|
245094
|
+
function parseReferencePath(value) {
|
|
245095
|
+
for (const candidate of [`$${value}`, `$(${value})`]) {
|
|
245096
|
+
let reparsed;
|
|
245097
|
+
try {
|
|
245098
|
+
reparsed = reparseAttribute(candidate);
|
|
245099
|
+
} catch (e4) {
|
|
245100
|
+
continue;
|
|
245101
|
+
}
|
|
245102
|
+
if (reparsed.length === 1 && reparsed[0].type === "macro") {
|
|
245103
|
+
return reparsed[0].path;
|
|
245104
|
+
}
|
|
245105
|
+
}
|
|
245106
|
+
throw new Error(`Could not parse "${value}" as a reference`);
|
|
245107
|
+
}
|
|
245108
|
+
const upgradeCollectElement = (context) => {
|
|
243627
245109
|
return (tree, file) => {
|
|
243628
|
-
|
|
243629
|
-
visit(tree, (node) => {
|
|
245110
|
+
visit(tree, (node, info) => {
|
|
243630
245111
|
if (!isDastElement(node)) {
|
|
243631
245112
|
return;
|
|
243632
245113
|
}
|
|
@@ -243636,50 +245117,45 @@ const upgradeCollectElement = () => {
|
|
|
243636
245117
|
if (node.attributes["componentTypes"]) {
|
|
243637
245118
|
renameAttrInPlace(node, "componentTypes", "componentType");
|
|
243638
245119
|
}
|
|
243639
|
-
|
|
243640
|
-
|
|
243641
|
-
|
|
243642
|
-
if (!fromValue.startsWith("$")) {
|
|
243643
|
-
node.attributes["from"].children = reparseAttribute(
|
|
243644
|
-
`$${fromValue}`
|
|
243645
|
-
);
|
|
243646
|
-
}
|
|
243647
|
-
} else if (node.attributes["target"]) {
|
|
243648
|
-
renameAttrInPlace(node, "target", "from");
|
|
243649
|
-
const fromValue = toXml(node.attributes["from"].children);
|
|
243650
|
-
if (!fromValue.startsWith("$")) {
|
|
243651
|
-
node.attributes["from"].children = reparseAttribute(
|
|
243652
|
-
`$${fromValue}`
|
|
243653
|
-
);
|
|
245120
|
+
for (const oldKey of ["source", "target"]) {
|
|
245121
|
+
if (!node.attributes[oldKey]) {
|
|
245122
|
+
continue;
|
|
243654
245123
|
}
|
|
245124
|
+
renameAttrInPlace(node, oldKey, "from");
|
|
245125
|
+
makeFromAReference(node, file);
|
|
245126
|
+
break;
|
|
243655
245127
|
}
|
|
243656
|
-
const
|
|
243657
|
-
if (!
|
|
245128
|
+
const assignNamesValue = readAssignNames(node);
|
|
245129
|
+
if (!assignNamesValue) {
|
|
245130
|
+
deleteAssignNames(node);
|
|
243658
245131
|
return;
|
|
243659
245132
|
}
|
|
243660
|
-
|
|
243661
|
-
|
|
243662
|
-
|
|
243663
|
-
|
|
243664
|
-
|
|
243665
|
-
|
|
243666
|
-
|
|
243667
|
-
`Expected attribute to be a single macro, got: ${JSON.stringify(attr)}`
|
|
243668
|
-
);
|
|
243669
|
-
}
|
|
243670
|
-
refsToRename[name2] = attr.path;
|
|
245133
|
+
const compositeName = registerCompositeAssignNames({
|
|
245134
|
+
node,
|
|
245135
|
+
assignNamesValue,
|
|
245136
|
+
fallbackBase: "collect",
|
|
245137
|
+
ancestorNames: namespaceChainOf(info.parents, context),
|
|
245138
|
+
context,
|
|
245139
|
+
file
|
|
243671
245140
|
});
|
|
245141
|
+
if (compositeName === void 0) {
|
|
245142
|
+
deleteAssignNames(node);
|
|
245143
|
+
return;
|
|
245144
|
+
}
|
|
245145
|
+
setCompositeName(node, compositeName);
|
|
243672
245146
|
});
|
|
243673
245147
|
replaceNode(tree, (node) => {
|
|
243674
245148
|
if (!isDastElement(node) || node.name !== "collect") {
|
|
243675
245149
|
return;
|
|
243676
245150
|
}
|
|
243677
|
-
const
|
|
243678
|
-
|
|
245151
|
+
const propKey = Object.keys(node.attributes).find(
|
|
245152
|
+
(key) => key.toLowerCase() === "prop"
|
|
245153
|
+
);
|
|
245154
|
+
const propName = propKey ? toXml(node.attributes[propKey].children).trim() : "";
|
|
243679
245155
|
if (!propName) {
|
|
243680
245156
|
return;
|
|
243681
245157
|
}
|
|
243682
|
-
delete node.attributes[
|
|
245158
|
+
delete node.attributes[propKey];
|
|
243683
245159
|
const setup = {
|
|
243684
245160
|
type: "element",
|
|
243685
245161
|
name: "setup",
|
|
@@ -243689,8 +245165,9 @@ const upgradeCollectElement = () => {
|
|
|
243689
245165
|
const componentType = toXml(
|
|
243690
245166
|
node.attributes["componentType"]?.children
|
|
243691
245167
|
).trim();
|
|
243692
|
-
const
|
|
243693
|
-
const
|
|
245168
|
+
const authoredListName = node.attributes["name"] ? toXml(node.attributes["name"].children).trim() : "";
|
|
245169
|
+
const listName = authoredListName && isValidReferenceableName(authoredListName) ? authoredListName : context.uniqueName("list");
|
|
245170
|
+
const collectName = context.uniqueName(`collect_${listName}`);
|
|
243694
245171
|
node.attributes["name"] = {
|
|
243695
245172
|
type: "attribute",
|
|
243696
245173
|
name: "name",
|
|
@@ -243700,7 +245177,11 @@ const upgradeCollectElement = () => {
|
|
|
243700
245177
|
if (!listType) {
|
|
243701
245178
|
file.message(
|
|
243702
245179
|
`Could not determine type for prop "${propName}" of component type "${componentType}". Using "math" as default.`,
|
|
243703
|
-
{
|
|
245180
|
+
{
|
|
245181
|
+
place: node.position,
|
|
245182
|
+
ruleId: "collect/unknown-prop-type",
|
|
245183
|
+
source: "v06-to-v07"
|
|
245184
|
+
}
|
|
243704
245185
|
);
|
|
243705
245186
|
listType = "math";
|
|
243706
245187
|
}
|
|
@@ -243718,30 +245199,81 @@ const upgradeCollectElement = () => {
|
|
|
243718
245199
|
extend: {
|
|
243719
245200
|
type: "attribute",
|
|
243720
245201
|
name: "extend",
|
|
243721
|
-
children:
|
|
243722
|
-
|
|
243723
|
-
|
|
245202
|
+
children: [
|
|
245203
|
+
{
|
|
245204
|
+
type: "macro",
|
|
245205
|
+
path: [
|
|
245206
|
+
...parseReferencePath(collectName),
|
|
245207
|
+
...propPathParts(propName, node, file)
|
|
245208
|
+
],
|
|
245209
|
+
attributes: {}
|
|
245210
|
+
}
|
|
245211
|
+
]
|
|
243724
245212
|
}
|
|
243725
245213
|
},
|
|
243726
245214
|
children: []
|
|
243727
245215
|
};
|
|
243728
245216
|
return [setup, list];
|
|
243729
245217
|
});
|
|
243730
|
-
visit(tree, (node) => {
|
|
243731
|
-
if (node.type !== "macro") {
|
|
243732
|
-
return;
|
|
243733
|
-
}
|
|
243734
|
-
if (!node.path.some((part) => refsToRename[part.name])) {
|
|
243735
|
-
return;
|
|
243736
|
-
}
|
|
243737
|
-
node.path = node.path.flatMap((part) => {
|
|
243738
|
-
return refsToRename[part.name] || [part];
|
|
243739
|
-
});
|
|
243740
|
-
});
|
|
243741
245218
|
};
|
|
243742
245219
|
};
|
|
245220
|
+
function makeFromAReference(node, file) {
|
|
245221
|
+
const attr = node.attributes["from"];
|
|
245222
|
+
const value = toXml(attr.children).trim();
|
|
245223
|
+
if (!value || value.startsWith("$")) {
|
|
245224
|
+
return;
|
|
245225
|
+
}
|
|
245226
|
+
try {
|
|
245227
|
+
attr.children = [
|
|
245228
|
+
{
|
|
245229
|
+
type: "macro",
|
|
245230
|
+
path: parseReferencePath(value),
|
|
245231
|
+
attributes: {}
|
|
245232
|
+
}
|
|
245233
|
+
];
|
|
245234
|
+
} catch {
|
|
245235
|
+
file.message(
|
|
245236
|
+
`Could not read "${value}" as the name of what <collect> collects from.`,
|
|
245237
|
+
{
|
|
245238
|
+
place: node.position,
|
|
245239
|
+
ruleId: "collect/unparsable-from",
|
|
245240
|
+
source: "v06-to-v07"
|
|
245241
|
+
}
|
|
245242
|
+
);
|
|
245243
|
+
}
|
|
245244
|
+
}
|
|
245245
|
+
function propPathParts(propName, node, file) {
|
|
245246
|
+
let parts;
|
|
245247
|
+
try {
|
|
245248
|
+
parts = parseReferencePath(propName);
|
|
245249
|
+
} catch {
|
|
245250
|
+
file.message(
|
|
245251
|
+
`<collect prop="${propName}"> could not be turned into a reference, so the generated list extends a path that needs fixing by hand.`,
|
|
245252
|
+
{
|
|
245253
|
+
place: node.position,
|
|
245254
|
+
ruleId: "collect/unparsable-prop",
|
|
245255
|
+
source: "v06-to-v07"
|
|
245256
|
+
}
|
|
245257
|
+
);
|
|
245258
|
+
return [{ type: "pathPart", name: propName, index: [] }];
|
|
245259
|
+
}
|
|
245260
|
+
parts.forEach(markAsPropAccess);
|
|
245261
|
+
return parts;
|
|
245262
|
+
}
|
|
243743
245263
|
const upgradePathSlashesToDots = () => {
|
|
243744
245264
|
return (tree, file) => {
|
|
245265
|
+
const warnParentPath = (original) => {
|
|
245266
|
+
file.message(
|
|
245267
|
+
`There is no equivalent to the $(../x) syntax; a best-guess was made when converting ${macroToString(
|
|
245268
|
+
original
|
|
245269
|
+
)}`,
|
|
245270
|
+
{
|
|
245271
|
+
place: original.position,
|
|
245272
|
+
ruleId: "no-v07-equivalent/parent-path",
|
|
245273
|
+
source: "v06-to-v07"
|
|
245274
|
+
}
|
|
245275
|
+
);
|
|
245276
|
+
};
|
|
243745
245277
|
visit(
|
|
243746
245278
|
tree,
|
|
243747
245279
|
// @ts-ignore
|
|
@@ -243752,44 +245284,17 @@ const upgradePathSlashesToDots = () => {
|
|
|
243752
245284
|
}
|
|
243753
245285
|
if (isDastElement(node)) {
|
|
243754
245286
|
for (const attr of Object.values(node.attributes)) {
|
|
243755
|
-
|
|
243756
|
-
if (isV06MacroOrFunctionMacro(child)) {
|
|
243757
|
-
macros.push(child);
|
|
243758
|
-
}
|
|
243759
|
-
}
|
|
245287
|
+
collectV06Macros(attr.children, macros);
|
|
243760
245288
|
}
|
|
243761
245289
|
}
|
|
243762
245290
|
for (const node2 of macros) {
|
|
243763
|
-
const macro = node2.type === "macro" ? v06MacroToV07Macro(node2) : v06FunctionMacroToV07FunctionMacro(
|
|
245291
|
+
const macro = node2.type === "macro" ? v06MacroToV07Macro(node2, warnParentPath) : v06FunctionMacroToV07FunctionMacro(
|
|
245292
|
+
node2,
|
|
245293
|
+
warnParentPath
|
|
245294
|
+
);
|
|
243764
245295
|
Object.keys(node2).forEach((key) => {
|
|
243765
245296
|
delete node2[key];
|
|
243766
245297
|
});
|
|
243767
|
-
if (macro.path.some((p2) => p2.name === "..")) {
|
|
243768
|
-
const newPath = [];
|
|
243769
|
-
let errorMessageWritten = false;
|
|
243770
|
-
for (const part of macro.path) {
|
|
243771
|
-
if (part.name === "..") {
|
|
243772
|
-
if (newPath.length > 0) {
|
|
243773
|
-
newPath.pop();
|
|
243774
|
-
if (!errorMessageWritten) {
|
|
243775
|
-
errorMessageWritten = true;
|
|
243776
|
-
file.message(
|
|
243777
|
-
`There is no equivalent to the $(../x) syntax; a best-guess was made when converting ${macroToString(
|
|
243778
|
-
macro
|
|
243779
|
-
)}`,
|
|
243780
|
-
{
|
|
243781
|
-
start: node2.position?.start,
|
|
243782
|
-
end: node2.position?.end
|
|
243783
|
-
}
|
|
243784
|
-
);
|
|
243785
|
-
}
|
|
243786
|
-
}
|
|
243787
|
-
continue;
|
|
243788
|
-
}
|
|
243789
|
-
newPath.push(part);
|
|
243790
|
-
}
|
|
243791
|
-
macro.path = newPath;
|
|
243792
|
-
}
|
|
243793
245298
|
Object.assign(node2, macro);
|
|
243794
245299
|
}
|
|
243795
245300
|
}
|
|
@@ -243798,24 +245303,54 @@ const upgradePathSlashesToDots = () => {
|
|
|
243798
245303
|
if (!isDastElement(node)) {
|
|
243799
245304
|
return;
|
|
243800
245305
|
}
|
|
245306
|
+
if (!ELEMENTS_WITH_REFERENCE_SOURCE.has(node.name.toLowerCase())) {
|
|
245307
|
+
return;
|
|
245308
|
+
}
|
|
243801
245309
|
const sourceAttr = node.attributes["source"];
|
|
243802
245310
|
if (!sourceAttr) {
|
|
243803
245311
|
return;
|
|
243804
245312
|
}
|
|
243805
245313
|
const sourceName = toXml(sourceAttr.children).trim();
|
|
243806
|
-
|
|
245314
|
+
const hasSlash = sourceName.includes("/");
|
|
245315
|
+
if (!hasSlash && !sourceName.includes(".")) {
|
|
243807
245316
|
return;
|
|
243808
245317
|
}
|
|
243809
245318
|
const reparsedSource = parseMacrosV06(`$(${sourceName})`);
|
|
243810
245319
|
if (reparsedSource.length !== 1 || reparsedSource[0].type !== "macro") {
|
|
243811
|
-
|
|
243812
|
-
|
|
243813
|
-
|
|
243814
|
-
|
|
245320
|
+
if (hasSlash) {
|
|
245321
|
+
file.message(
|
|
245322
|
+
`Could not reparse "source" attribute that contains a slash: "${sourceName}".`,
|
|
245323
|
+
{
|
|
245324
|
+
place: node.position,
|
|
245325
|
+
ruleId: "copy/unparsable-source",
|
|
245326
|
+
source: "v06-to-v07"
|
|
245327
|
+
}
|
|
245328
|
+
);
|
|
245329
|
+
}
|
|
243815
245330
|
return;
|
|
243816
245331
|
}
|
|
243817
|
-
const upgradedSource = v06MacroToV07Macro(
|
|
245332
|
+
const upgradedSource = v06MacroToV07Macro(
|
|
245333
|
+
reparsedSource[0],
|
|
245334
|
+
() => file.message(
|
|
245335
|
+
`There is no equivalent to the ../x syntax; a best-guess was made when converting source="${sourceName}"`,
|
|
245336
|
+
{
|
|
245337
|
+
place: node.position,
|
|
245338
|
+
ruleId: "no-v07-equivalent/parent-path",
|
|
245339
|
+
source: "v06-to-v07"
|
|
245340
|
+
}
|
|
245341
|
+
)
|
|
245342
|
+
);
|
|
245343
|
+
markRawPropPositions(
|
|
245344
|
+
node,
|
|
245345
|
+
"source",
|
|
245346
|
+
upgradedSource.path.flatMap(
|
|
245347
|
+
(part, index2) => isPropAccess(part) ? [index2] : []
|
|
245348
|
+
)
|
|
245349
|
+
);
|
|
243818
245350
|
const newSourceName = toXml(upgradedSource.path);
|
|
245351
|
+
if (newSourceName === sourceName) {
|
|
245352
|
+
return;
|
|
245353
|
+
}
|
|
243819
245354
|
sourceAttr.children = [
|
|
243820
245355
|
{
|
|
243821
245356
|
type: "text",
|
|
@@ -243826,12 +245361,44 @@ const upgradePathSlashesToDots = () => {
|
|
|
243826
245361
|
});
|
|
243827
245362
|
};
|
|
243828
245363
|
};
|
|
243829
|
-
function
|
|
243830
|
-
const
|
|
245364
|
+
function collectV06Macros(nodes, macros) {
|
|
245365
|
+
for (const node of nodes) {
|
|
245366
|
+
if (isV06MacroOrFunctionMacro(node)) {
|
|
245367
|
+
macros.push(node);
|
|
245368
|
+
}
|
|
245369
|
+
}
|
|
245370
|
+
}
|
|
245371
|
+
const ELEMENTS_WITH_REFERENCE_SOURCE = /* @__PURE__ */ new Set(["copy", "collect", "extract"]);
|
|
245372
|
+
function collapseParentPathParts(path, warn) {
|
|
245373
|
+
if (!path.some((part) => part.name === "..")) {
|
|
245374
|
+
return path;
|
|
245375
|
+
}
|
|
245376
|
+
const newPath = [];
|
|
245377
|
+
let warned = false;
|
|
245378
|
+
for (const part of path) {
|
|
245379
|
+
if (part.name === "..") {
|
|
245380
|
+
if (newPath.length > 0) {
|
|
245381
|
+
newPath.pop();
|
|
245382
|
+
}
|
|
245383
|
+
if (!warned) {
|
|
245384
|
+
warned = true;
|
|
245385
|
+
warn();
|
|
245386
|
+
}
|
|
245387
|
+
continue;
|
|
245388
|
+
}
|
|
245389
|
+
newPath.push(part);
|
|
245390
|
+
}
|
|
245391
|
+
return newPath;
|
|
245392
|
+
}
|
|
245393
|
+
function v06MacroToV07Macro(macro, warn, reportAs = macro) {
|
|
245394
|
+
const path = collapseParentPathParts(
|
|
245395
|
+
flattenedAccessedProps(macro, warn),
|
|
245396
|
+
() => warn(reportAs)
|
|
245397
|
+
);
|
|
243831
245398
|
const { accessedProp, version: version2, attributes, ...rest2 } = macro;
|
|
243832
245399
|
return {
|
|
243833
245400
|
...rest2,
|
|
243834
|
-
attributes: v06AttributeToV07Attribute(mergeAttributes(macro)),
|
|
245401
|
+
attributes: v06AttributeToV07Attribute(mergeAttributes(macro), warn),
|
|
243835
245402
|
path
|
|
243836
245403
|
};
|
|
243837
245404
|
}
|
|
@@ -243849,35 +245416,56 @@ function mergeAttributes(macro) {
|
|
|
243849
245416
|
}
|
|
243850
245417
|
return ret;
|
|
243851
245418
|
}
|
|
243852
|
-
function v06FunctionMacroToV07FunctionMacro(funcMacro) {
|
|
243853
|
-
const macro = v06MacroToV07Macro(funcMacro.macro);
|
|
245419
|
+
function v06FunctionMacroToV07FunctionMacro(funcMacro, warn) {
|
|
245420
|
+
const macro = v06MacroToV07Macro(funcMacro.macro, warn, funcMacro);
|
|
243854
245421
|
return {
|
|
243855
245422
|
type: "function",
|
|
243856
245423
|
path: macro.path,
|
|
243857
|
-
|
|
243858
|
-
// the syntax of function arguments.
|
|
243859
|
-
input: funcMacro.input,
|
|
245424
|
+
input: convertArguments(funcMacro.input, warn),
|
|
243860
245425
|
position: funcMacro.position
|
|
243861
245426
|
};
|
|
243862
245427
|
}
|
|
243863
|
-
function
|
|
245428
|
+
function convertArguments(input, warn) {
|
|
245429
|
+
if (!input) {
|
|
245430
|
+
return null;
|
|
245431
|
+
}
|
|
245432
|
+
return input.map(
|
|
245433
|
+
(argument) => argument.map((node) => {
|
|
245434
|
+
if (isV06MacroOrFunctionMacro(node)) {
|
|
245435
|
+
return node.type === "macro" ? v06MacroToV07Macro(node, warn) : v06FunctionMacroToV07FunctionMacro(node, warn);
|
|
245436
|
+
}
|
|
245437
|
+
return node;
|
|
245438
|
+
})
|
|
245439
|
+
);
|
|
245440
|
+
}
|
|
245441
|
+
function v06IndexToV07Index(index2, warn) {
|
|
243864
245442
|
return index2.map((ind) => ({
|
|
243865
245443
|
...ind,
|
|
243866
|
-
value
|
|
243867
|
-
|
|
243868
|
-
)
|
|
245444
|
+
// @ts-ignore -- see the note on `value` below
|
|
245445
|
+
// The DAST types say an index holds only text and plain macros, but the v0.6
|
|
245446
|
+
// parser does put a function macro in one (`$list[$$(g/h)(2)]`), so both have
|
|
245447
|
+
// to be converted here — this is the only pass that reaches them.
|
|
245448
|
+
value: ind.value.map((v2) => {
|
|
245449
|
+
if (v2.type === "macro") {
|
|
245450
|
+
return v06MacroToV07Macro(v2, warn);
|
|
245451
|
+
}
|
|
245452
|
+
if (v2.type === "function") {
|
|
245453
|
+
return v06FunctionMacroToV07FunctionMacro(v2, warn);
|
|
245454
|
+
}
|
|
245455
|
+
return v2;
|
|
245456
|
+
})
|
|
243869
245457
|
}));
|
|
243870
245458
|
}
|
|
243871
|
-
function v06AttributeToV07Attribute(attrs) {
|
|
245459
|
+
function v06AttributeToV07Attribute(attrs, warn) {
|
|
243872
245460
|
const ret = {};
|
|
243873
245461
|
attrs = Array.isArray(attrs) ? attrs : Object.values(attrs);
|
|
243874
245462
|
for (const attr of attrs) {
|
|
243875
245463
|
const children = attr.children.map((c2) => {
|
|
243876
245464
|
if (c2.type === "macro") {
|
|
243877
|
-
return v06MacroToV07Macro(c2);
|
|
245465
|
+
return v06MacroToV07Macro(c2, warn);
|
|
243878
245466
|
}
|
|
243879
245467
|
if (c2.type === "function") {
|
|
243880
|
-
return v06FunctionMacroToV07FunctionMacro(c2);
|
|
245468
|
+
return v06FunctionMacroToV07FunctionMacro(c2, warn);
|
|
243881
245469
|
}
|
|
243882
245470
|
return c2;
|
|
243883
245471
|
});
|
|
@@ -243888,15 +245476,17 @@ function v06AttributeToV07Attribute(attrs) {
|
|
|
243888
245476
|
}
|
|
243889
245477
|
return ret;
|
|
243890
245478
|
}
|
|
243891
|
-
function flattenedAccessedProps(macro) {
|
|
245479
|
+
function flattenedAccessedProps(macro, warn) {
|
|
243892
245480
|
const path = macro.path.map((p2) => {
|
|
243893
245481
|
return {
|
|
243894
245482
|
...p2,
|
|
243895
|
-
index: v06IndexToV07Index(p2.index)
|
|
245483
|
+
index: v06IndexToV07Index(p2.index, warn)
|
|
243896
245484
|
};
|
|
243897
245485
|
});
|
|
243898
245486
|
if (macro.accessedProp) {
|
|
243899
|
-
|
|
245487
|
+
const propParts = flattenedAccessedProps(macro.accessedProp, warn);
|
|
245488
|
+
propParts.forEach(markAsPropAccess);
|
|
245489
|
+
return [...path, ...propParts];
|
|
243900
245490
|
}
|
|
243901
245491
|
return path;
|
|
243902
245492
|
}
|
|
@@ -244651,9 +246241,26 @@ async function createCoreForLookup({ dast }) {
|
|
|
244651
246241
|
const wasmBuffer = (await import("./lib_doenetml_worker_bg-BJtDoC-f.js")).default;
|
|
244652
246242
|
await __wbg_init({ module_or_path: wasmBuffer });
|
|
244653
246243
|
const rustCore = PublicDoenetMLCore.new();
|
|
244654
|
-
|
|
244655
|
-
|
|
244656
|
-
|
|
246244
|
+
let core2;
|
|
246245
|
+
async function releaseCores() {
|
|
246246
|
+
try {
|
|
246247
|
+
await core2?.terminate();
|
|
246248
|
+
} catch (e4) {
|
|
246249
|
+
}
|
|
246250
|
+
try {
|
|
246251
|
+
rustCore.free();
|
|
246252
|
+
} catch (e4) {
|
|
246253
|
+
}
|
|
246254
|
+
}
|
|
246255
|
+
let normalizedRoot;
|
|
246256
|
+
try {
|
|
246257
|
+
dast = normalizeDocumentDast(structuredClone(dast), true);
|
|
246258
|
+
rustCore.set_source(dast, toXml(dast));
|
|
246259
|
+
normalizedRoot = rustCore.return_normalized_dast_root();
|
|
246260
|
+
} catch (e4) {
|
|
246261
|
+
await releaseCores();
|
|
246262
|
+
throw e4;
|
|
246263
|
+
}
|
|
244657
246264
|
function resolvePath(path, origin, skip_parent_search) {
|
|
244658
246265
|
return rustCore.resolve_path(path, origin, skip_parent_search);
|
|
244659
246266
|
}
|
|
@@ -244664,44 +246271,62 @@ async function createCoreForLookup({ dast }) {
|
|
|
244664
246271
|
rustCore.delete_nodes_from_resolver(node_list);
|
|
244665
246272
|
}
|
|
244666
246273
|
const flags = { ...defaultFlags };
|
|
244667
|
-
|
|
244668
|
-
|
|
244669
|
-
|
|
244670
|
-
|
|
244671
|
-
|
|
244672
|
-
|
|
244673
|
-
|
|
244674
|
-
|
|
244675
|
-
|
|
244676
|
-
|
|
244677
|
-
|
|
244678
|
-
|
|
244679
|
-
|
|
244680
|
-
|
|
244681
|
-
|
|
244682
|
-
|
|
244683
|
-
|
|
244684
|
-
|
|
244685
|
-
|
|
244686
|
-
|
|
244687
|
-
|
|
244688
|
-
|
|
244689
|
-
|
|
244690
|
-
|
|
244691
|
-
|
|
244692
|
-
|
|
244693
|
-
|
|
244694
|
-
|
|
244695
|
-
|
|
244696
|
-
|
|
246274
|
+
try {
|
|
246275
|
+
core2 = new PublicDoenetMLCore$1();
|
|
246276
|
+
core2.setSource(toXml(dast));
|
|
246277
|
+
core2.setFlags(flags);
|
|
246278
|
+
await core2.initializeWorker({
|
|
246279
|
+
activityId: "",
|
|
246280
|
+
docId: "1",
|
|
246281
|
+
requestedVariantIndex: 1,
|
|
246282
|
+
attemptNumber: 1,
|
|
246283
|
+
normalizedRoot,
|
|
246284
|
+
addNodesToResolver,
|
|
246285
|
+
deleteNodesFromResolver,
|
|
246286
|
+
resolvePath
|
|
246287
|
+
});
|
|
246288
|
+
const dastResult = await core2.createCoreGenerateDast(
|
|
246289
|
+
{
|
|
246290
|
+
coreId: "",
|
|
246291
|
+
cid: "",
|
|
246292
|
+
initializeCounters: {},
|
|
246293
|
+
theme: "light"
|
|
246294
|
+
},
|
|
246295
|
+
() => null,
|
|
246296
|
+
() => null,
|
|
246297
|
+
() => null,
|
|
246298
|
+
() => null,
|
|
246299
|
+
() => null,
|
|
246300
|
+
() => null,
|
|
246301
|
+
async () => ({ allowView: true })
|
|
246302
|
+
);
|
|
246303
|
+
if (!dastResult.success) {
|
|
246304
|
+
throw Error(dastResult.errMsg);
|
|
246305
|
+
}
|
|
246306
|
+
} catch (e4) {
|
|
246307
|
+
await releaseCores();
|
|
246308
|
+
throw e4;
|
|
246309
|
+
}
|
|
246310
|
+
const readyCore = core2;
|
|
246311
|
+
if (!readyCore) {
|
|
246312
|
+
await releaseCores();
|
|
246313
|
+
throw Error("The DoenetML core was not initialized.");
|
|
244697
246314
|
}
|
|
244698
246315
|
function resolvePathToNodeIdx(name2, origin = 0) {
|
|
244699
246316
|
if (name2.startsWith("$")) {
|
|
244700
246317
|
name2 = name2.slice(1);
|
|
244701
246318
|
}
|
|
244702
|
-
return resolvePathImmediatelyToNodeIdx(
|
|
246319
|
+
return resolvePathImmediatelyToNodeIdx(
|
|
246320
|
+
name2,
|
|
246321
|
+
rustCore,
|
|
246322
|
+
readyCore,
|
|
246323
|
+
origin
|
|
246324
|
+
);
|
|
246325
|
+
}
|
|
246326
|
+
async function dispose() {
|
|
246327
|
+
await releaseCores();
|
|
244703
246328
|
}
|
|
244704
|
-
return { core:
|
|
246329
|
+
return { core: readyCore, rustCore, resolvePathToNodeIdx, dispose };
|
|
244705
246330
|
}
|
|
244706
246331
|
const upgradeCopySyntax = () => {
|
|
244707
246332
|
return async (tree, file) => {
|
|
@@ -244718,91 +246343,128 @@ const upgradeCopySyntax = () => {
|
|
|
244718
246343
|
if (skip) {
|
|
244719
246344
|
return;
|
|
244720
246345
|
}
|
|
244721
|
-
|
|
244722
|
-
|
|
244723
|
-
|
|
244724
|
-
|
|
244725
|
-
|
|
244726
|
-
|
|
244727
|
-
|
|
244728
|
-
|
|
244729
|
-
|
|
244730
|
-
}
|
|
244731
|
-
if (node.attributes["prop"]) {
|
|
244732
|
-
const propName = toXml(node.attributes["prop"].children).trim();
|
|
244733
|
-
if (propName) {
|
|
244734
|
-
referentName += `.${propName}`;
|
|
244735
|
-
}
|
|
244736
|
-
delete node.attributes["prop"];
|
|
244737
|
-
}
|
|
244738
|
-
if (node.attributes["assignNames"]) {
|
|
244739
|
-
if (node.attributes["name"]) {
|
|
244740
|
-
file.message(
|
|
244741
|
-
`The <copy> tag with source="${referentName}" has both "name" and "assignNames" attributes. "name" will be ignored.`,
|
|
244742
|
-
node.position?.start
|
|
244743
|
-
);
|
|
244744
|
-
delete node.attributes["name"];
|
|
246346
|
+
let core2;
|
|
246347
|
+
try {
|
|
246348
|
+
core2 = await createCoreForLookup({ dast: tree });
|
|
246349
|
+
} catch (e4) {
|
|
246350
|
+
file.message(
|
|
246351
|
+
`Could not load the document to work out what the <copy> tags refer to, so they were left as they are: ${e4}`,
|
|
246352
|
+
{
|
|
246353
|
+
ruleId: "copy/could-not-load-document",
|
|
246354
|
+
source: "v06-to-v07"
|
|
244745
246355
|
}
|
|
244746
|
-
|
|
246356
|
+
);
|
|
246357
|
+
return;
|
|
246358
|
+
}
|
|
246359
|
+
try {
|
|
246360
|
+
await resolveCopyTags(core2, tree, file);
|
|
246361
|
+
} finally {
|
|
246362
|
+
await core2.dispose();
|
|
246363
|
+
}
|
|
246364
|
+
};
|
|
246365
|
+
};
|
|
246366
|
+
async function resolveCopyTags(core2, tree, file) {
|
|
246367
|
+
const referenced = [];
|
|
246368
|
+
visit(tree, (node) => {
|
|
246369
|
+
if (!isDastElement(node) || node.name !== "copy") {
|
|
246370
|
+
return;
|
|
246371
|
+
}
|
|
246372
|
+
if (Object.keys(node.attributes).some(
|
|
246373
|
+
(key) => key.toLowerCase() === "uri"
|
|
246374
|
+
)) {
|
|
246375
|
+
return;
|
|
246376
|
+
}
|
|
246377
|
+
let referentName = toXml(node.attributes["source"]?.children).trim();
|
|
246378
|
+
if (!referentName) {
|
|
246379
|
+
return;
|
|
246380
|
+
}
|
|
246381
|
+
const propKey = Object.keys(node.attributes).find(
|
|
246382
|
+
(key) => key.toLowerCase() === "prop"
|
|
246383
|
+
);
|
|
246384
|
+
if (propKey) {
|
|
246385
|
+
const propName = toXml(node.attributes[propKey].children).trim();
|
|
246386
|
+
if (propName) {
|
|
246387
|
+
referentName += `.${propName}`;
|
|
244747
246388
|
}
|
|
244748
|
-
|
|
244749
|
-
|
|
244750
|
-
referentType: findReferentType(core2, referentName),
|
|
244751
|
-
referentName
|
|
244752
|
-
});
|
|
244753
|
-
});
|
|
244754
|
-
for (let {
|
|
246389
|
+
}
|
|
246390
|
+
referenced.push({
|
|
244755
246391
|
node,
|
|
244756
|
-
referentType:
|
|
244757
|
-
referentName
|
|
244758
|
-
|
|
244759
|
-
|
|
244760
|
-
|
|
244761
|
-
|
|
244762
|
-
|
|
244763
|
-
|
|
244764
|
-
|
|
244765
|
-
|
|
244766
|
-
|
|
244767
|
-
|
|
244768
|
-
|
|
244769
|
-
|
|
244770
|
-
|
|
244771
|
-
node.attributes[targetTag].children = reparseAttribute(referentName);
|
|
244772
|
-
node.name = referentType;
|
|
244773
|
-
} catch (e4) {
|
|
244774
|
-
file.message(
|
|
244775
|
-
`Could not resolve referent type for <copy> tag with source="${referentName}": ${e4}`,
|
|
244776
|
-
node.position?.start
|
|
244777
|
-
);
|
|
244778
|
-
continue;
|
|
246392
|
+
referentType: findReferentType(core2, referentName),
|
|
246393
|
+
referentName,
|
|
246394
|
+
propKey
|
|
246395
|
+
});
|
|
246396
|
+
});
|
|
246397
|
+
for (let {
|
|
246398
|
+
node,
|
|
246399
|
+
referentType: referentPromise,
|
|
246400
|
+
referentName,
|
|
246401
|
+
propKey
|
|
246402
|
+
} of referenced) {
|
|
246403
|
+
try {
|
|
246404
|
+
const referentType = await referentPromise;
|
|
246405
|
+
if (propKey) {
|
|
246406
|
+
delete node.attributes[propKey];
|
|
244779
246407
|
}
|
|
246408
|
+
const linkKey = Object.keys(node.attributes).find(
|
|
246409
|
+
(key) => key.toLowerCase() === "link"
|
|
246410
|
+
);
|
|
246411
|
+
const targetTag = linkKey ? isV06True(node.attributes[linkKey]) ? "extend" : "copy" : isModuleComponentType(referentType) ? "copy" : "extend";
|
|
246412
|
+
if (linkKey) {
|
|
246413
|
+
delete node.attributes[linkKey];
|
|
246414
|
+
}
|
|
246415
|
+
renameAttrInPlace(node, "source", targetTag);
|
|
246416
|
+
const bareName = referentName.startsWith("$") ? referentName.slice(1) : referentName;
|
|
246417
|
+
node.attributes[targetTag].children = [
|
|
246418
|
+
{
|
|
246419
|
+
type: "macro",
|
|
246420
|
+
path: parseReferencePath(bareName),
|
|
246421
|
+
attributes: {}
|
|
246422
|
+
}
|
|
246423
|
+
];
|
|
246424
|
+
node.name = referentType;
|
|
246425
|
+
} catch (e4) {
|
|
246426
|
+
file.message(
|
|
246427
|
+
`Could not resolve referent type for <copy> tag with source="${referentName}": ${e4}`,
|
|
246428
|
+
{
|
|
246429
|
+
place: node.position,
|
|
246430
|
+
ruleId: "copy/unresolved-referent",
|
|
246431
|
+
source: "v06-to-v07"
|
|
246432
|
+
}
|
|
246433
|
+
);
|
|
246434
|
+
continue;
|
|
244780
246435
|
}
|
|
244781
|
-
};
|
|
244782
|
-
};
|
|
244783
|
-
async function findReferentType(core2, referentName) {
|
|
244784
|
-
if (!referentName.startsWith("$")) {
|
|
244785
|
-
referentName = `$${referentName}`;
|
|
244786
246436
|
}
|
|
244787
|
-
|
|
244788
|
-
|
|
244789
|
-
|
|
246437
|
+
}
|
|
246438
|
+
async function findReferentType(core2, referentName) {
|
|
246439
|
+
const bare = referentName.startsWith("$") ? referentName.slice(1) : referentName;
|
|
246440
|
+
let path;
|
|
246441
|
+
try {
|
|
246442
|
+
path = parseReferencePath(bare);
|
|
246443
|
+
} catch (e4) {
|
|
244790
246444
|
throw new Error(`Could not parse referent name "${referentName}"`);
|
|
244791
246445
|
}
|
|
244792
246446
|
let unresolvedIndex = [];
|
|
244793
246447
|
let unresolvedProps = [];
|
|
244794
246448
|
let referentType = void 0;
|
|
244795
|
-
for (let i2 = path.length; i2 > 0; i2--) {
|
|
246449
|
+
search: for (let i2 = path.length; i2 > 0; i2--) {
|
|
244796
246450
|
const pathParts = path.slice(0, i2);
|
|
244797
|
-
const
|
|
244798
|
-
|
|
244799
|
-
|
|
244800
|
-
|
|
244801
|
-
|
|
244802
|
-
|
|
244803
|
-
|
|
244804
|
-
|
|
246451
|
+
for (const keepIndices of [true, false]) {
|
|
246452
|
+
if (keepIndices && !hasOnlyLiteralIndices(pathParts)) {
|
|
246453
|
+
continue;
|
|
246454
|
+
}
|
|
246455
|
+
const pathStr = keepIndices ? printPath(pathParts) : printPathWithoutIndices(pathParts);
|
|
246456
|
+
const referentIdx = await core2.resolvePathToNodeIdx(pathStr);
|
|
246457
|
+
if (referentIdx === -1) {
|
|
246458
|
+
continue;
|
|
246459
|
+
}
|
|
246460
|
+
const foundType = core2.core.core?.components?.[referentIdx]?.componentType;
|
|
246461
|
+
if (!foundType || foundType.startsWith("_")) {
|
|
246462
|
+
continue;
|
|
244805
246463
|
}
|
|
246464
|
+
referentType = foundType;
|
|
246465
|
+
unresolvedIndex = keepIndices ? [] : pathParts.flatMap((part) => part.index);
|
|
246466
|
+
unresolvedProps = path.slice(i2);
|
|
246467
|
+
break search;
|
|
244806
246468
|
}
|
|
244807
246469
|
}
|
|
244808
246470
|
if (unresolvedIndex.length !== 0) {
|
|
@@ -244824,14 +246486,287 @@ async function findReferentType(core2, referentName) {
|
|
|
244824
246486
|
}
|
|
244825
246487
|
throw new Error(`Could not find referent type for "${referentName}"`);
|
|
244826
246488
|
}
|
|
246489
|
+
function hasOnlyLiteralIndices(pathParts) {
|
|
246490
|
+
return pathParts.every(
|
|
246491
|
+
(part) => part.index.every(
|
|
246492
|
+
(index2) => index2.value.length === 1 && index2.value[0].type === "text" && /^\d+$/.test(index2.value[0].value.trim())
|
|
246493
|
+
)
|
|
246494
|
+
);
|
|
246495
|
+
}
|
|
246496
|
+
function printPath(pathParts) {
|
|
246497
|
+
return pathParts.map(
|
|
246498
|
+
(part) => part.name + part.index.map((index2) => `[${toXml(index2.value).trim()}]`).join("")
|
|
246499
|
+
).join(".");
|
|
246500
|
+
}
|
|
244827
246501
|
function printPathWithoutIndices(pathParts) {
|
|
244828
246502
|
return pathParts.map((part) => {
|
|
244829
246503
|
return part.name;
|
|
244830
246504
|
}).join(".");
|
|
244831
246505
|
}
|
|
244832
|
-
const
|
|
246506
|
+
const upgradeCopyElements = (context) => {
|
|
246507
|
+
return (tree, file) => {
|
|
246508
|
+
visit(tree, (node, info) => {
|
|
246509
|
+
if (!isDastElement(node)) {
|
|
246510
|
+
return;
|
|
246511
|
+
}
|
|
246512
|
+
if (node.name.toLowerCase() === "ref") {
|
|
246513
|
+
warnAboutExternalRef(node, file);
|
|
246514
|
+
return;
|
|
246515
|
+
}
|
|
246516
|
+
if (node.name.toLowerCase() !== "copy") {
|
|
246517
|
+
return;
|
|
246518
|
+
}
|
|
246519
|
+
convertAssignNames(
|
|
246520
|
+
node,
|
|
246521
|
+
namespaceChainOf(info.parents, context),
|
|
246522
|
+
context,
|
|
246523
|
+
file
|
|
246524
|
+
);
|
|
246525
|
+
const uriKey = findKey$1(node, "uri");
|
|
246526
|
+
if (!uriKey) {
|
|
246527
|
+
return;
|
|
246528
|
+
}
|
|
246529
|
+
const uri = toXml(node.attributes[uriKey].children).trim();
|
|
246530
|
+
if (!uri.toLowerCase().startsWith("doenet:")) {
|
|
246531
|
+
return;
|
|
246532
|
+
}
|
|
246533
|
+
const passedAttributes = Object.keys(node.attributes).filter(
|
|
246534
|
+
(key) => !COPY_OWN_ATTRIBUTES.has(key.toLowerCase())
|
|
246535
|
+
);
|
|
246536
|
+
if (passedAttributes.length === 0) {
|
|
246537
|
+
file.message(
|
|
246538
|
+
`<copy uri="${uri}"> could not be converted: v0.7 loads external content with a "copy" attribute on an element of the same component type as the external document, and this copy passes no attributes to indicate what that type is. Replace it with, for example, <problem copy="doenet:<contentId>" /> once the target has been converted.`,
|
|
246539
|
+
{
|
|
246540
|
+
place: node.position,
|
|
246541
|
+
ruleId: "external-copy/unknown-component-type",
|
|
246542
|
+
source: "v06-to-v07"
|
|
246543
|
+
}
|
|
246544
|
+
);
|
|
246545
|
+
return;
|
|
246546
|
+
}
|
|
246547
|
+
const narrowing = Object.keys(node.attributes).filter(
|
|
246548
|
+
(key) => COPY_NARROWING_ATTRIBUTES.has(key.toLowerCase())
|
|
246549
|
+
);
|
|
246550
|
+
if (narrowing.length > 0) {
|
|
246551
|
+
file.message(
|
|
246552
|
+
`<copy uri="${uri}"> could not be converted because it also sets ${narrowing.join(
|
|
246553
|
+
", "
|
|
246554
|
+
)}, which changes what it copies; v0.7 has no way to say that on the element that loads an external document. Convert it by hand.`,
|
|
246555
|
+
{
|
|
246556
|
+
place: node.position,
|
|
246557
|
+
ruleId: "external-copy/narrowed",
|
|
246558
|
+
source: "v06-to-v07"
|
|
246559
|
+
}
|
|
246560
|
+
);
|
|
246561
|
+
return;
|
|
246562
|
+
}
|
|
246563
|
+
const dropped = Object.keys(node.attributes).filter(
|
|
246564
|
+
(key) => COPY_CONTROL_ATTRIBUTES.has(key.toLowerCase())
|
|
246565
|
+
);
|
|
246566
|
+
for (const key of dropped) {
|
|
246567
|
+
delete node.attributes[key];
|
|
246568
|
+
}
|
|
246569
|
+
const linkKey = findKey$1(node, "link");
|
|
246570
|
+
const targetTag = linkKey && isV06True(node.attributes[linkKey]) ? "extend" : "copy";
|
|
246571
|
+
if (linkKey) {
|
|
246572
|
+
delete node.attributes[linkKey];
|
|
246573
|
+
}
|
|
246574
|
+
node.name = "module";
|
|
246575
|
+
renameAttrInPlace(node, uriKey, targetTag);
|
|
246576
|
+
if (dropped.length > 0) {
|
|
246577
|
+
file.message(
|
|
246578
|
+
`Dropped ${dropped.join(
|
|
246579
|
+
", "
|
|
246580
|
+
)} from <copy uri="${uri}">: they controlled how v0.6 copied, and a <module ${targetTag}="doenet:..."> has no equivalent.`,
|
|
246581
|
+
{
|
|
246582
|
+
place: node.position,
|
|
246583
|
+
ruleId: "external-copy/dropped-copy-controls",
|
|
246584
|
+
source: "v06-to-v07"
|
|
246585
|
+
}
|
|
246586
|
+
);
|
|
246587
|
+
}
|
|
246588
|
+
file.message(
|
|
246589
|
+
`Converted <copy uri="${uri}"> to <module ${targetTag}="${uri}">, passing ${passedAttributes.join(", ")}. The identifier in the URI is a v0.6 one; replace it with the v0.7 content id of the converted module, and check that the target really is a <module>.`,
|
|
246590
|
+
{
|
|
246591
|
+
place: node.position,
|
|
246592
|
+
ruleId: "external-copy/needs-new-content-id",
|
|
246593
|
+
source: "v06-to-v07"
|
|
246594
|
+
}
|
|
246595
|
+
);
|
|
246596
|
+
});
|
|
246597
|
+
};
|
|
246598
|
+
};
|
|
246599
|
+
const COPY_NARROWING_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
246600
|
+
"componentindex",
|
|
246601
|
+
"createcomponentoftype",
|
|
246602
|
+
"numcomponents",
|
|
246603
|
+
"obtainpropfromcomposite",
|
|
246604
|
+
"prop",
|
|
246605
|
+
"propindex",
|
|
246606
|
+
"removeemptyarrayentries",
|
|
246607
|
+
"source",
|
|
246608
|
+
"sourceindex",
|
|
246609
|
+
"sourcesubnames",
|
|
246610
|
+
"sourcesubnamescomponentindex",
|
|
246611
|
+
"target",
|
|
246612
|
+
"tname"
|
|
246613
|
+
]);
|
|
246614
|
+
const COPY_CONTROL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
246615
|
+
"assignnamesskip",
|
|
246616
|
+
"assignnewnamespaces",
|
|
246617
|
+
"sourceattributestoignore"
|
|
246618
|
+
]);
|
|
246619
|
+
const COPY_OWN_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
246620
|
+
"assignnames",
|
|
246621
|
+
"assignnamesskip",
|
|
246622
|
+
"assignnewnamespaces",
|
|
246623
|
+
"componentindex",
|
|
246624
|
+
"createcomponentoftype",
|
|
246625
|
+
"link",
|
|
246626
|
+
"name",
|
|
246627
|
+
"newnamespace",
|
|
246628
|
+
"numcomponents",
|
|
246629
|
+
"obtainpropfromcomposite",
|
|
246630
|
+
"prop",
|
|
246631
|
+
"propindex",
|
|
246632
|
+
"removeemptyarrayentries",
|
|
246633
|
+
"source",
|
|
246634
|
+
"sourceattributestoignore",
|
|
246635
|
+
"sourceindex",
|
|
246636
|
+
"sourcesubnames",
|
|
246637
|
+
"sourcesubnamescomponentindex",
|
|
246638
|
+
"target",
|
|
246639
|
+
"tname",
|
|
246640
|
+
"uri"
|
|
246641
|
+
]);
|
|
246642
|
+
function convertAssignNames(node, ancestorNames, context, file) {
|
|
246643
|
+
const assignNamesValue = readAssignNames(node);
|
|
246644
|
+
if (!assignNamesValue) {
|
|
246645
|
+
deleteAssignNames(node);
|
|
246646
|
+
return;
|
|
246647
|
+
}
|
|
246648
|
+
const origin = {
|
|
246649
|
+
elementName: node.name,
|
|
246650
|
+
position: node.position,
|
|
246651
|
+
ancestorNames,
|
|
246652
|
+
element: node
|
|
246653
|
+
};
|
|
246654
|
+
const parsed = breakStringInPiecesBySpacesOrParens(assignNamesValue);
|
|
246655
|
+
const names = parsed.success && parsed.pieces.every((p2) => typeof p2 === "string") ? parsed.pieces : void 0;
|
|
246656
|
+
if (!names || names.length !== 1) {
|
|
246657
|
+
file.message(
|
|
246658
|
+
`assignNames="${assignNamesValue}" on <${node.name}> could not be converted: the names refer to the replacements of the copied component, and this converter cannot tell how many of those there are. References to those names need fixing by hand.`,
|
|
246659
|
+
{
|
|
246660
|
+
place: node.position,
|
|
246661
|
+
ruleId: "copy/unmapped-assign-names",
|
|
246662
|
+
source: "v06-to-v07"
|
|
246663
|
+
}
|
|
246664
|
+
);
|
|
246665
|
+
deleteAssignNames(node);
|
|
246666
|
+
return;
|
|
246667
|
+
}
|
|
246668
|
+
const [assignedName] = names;
|
|
246669
|
+
const existingName = toXml(
|
|
246670
|
+
node.attributes[findKey$1(node, "name") ?? ""]?.children ?? []
|
|
246671
|
+
).trim();
|
|
246672
|
+
const nameIsUnusable = !isValidReferenceableName(assignedName);
|
|
246673
|
+
const nameBelongsToAnother = nameIsUnusable || nameTakenInScope(context, assignedName, ancestorNames);
|
|
246674
|
+
const nameIsTaken = nameBelongsToAnother || context.existingNames.has(assignedName) || context.claimedNames.has(assignedName);
|
|
246675
|
+
if (existingName && !isValidReferenceableName(existingName)) {
|
|
246676
|
+
file.message(
|
|
246677
|
+
`<${node.name}> is called "${existingName}", which v0.7 cannot use as a name — it must start with a letter and hold only letters, digits, underscores and hyphens. A generated name was used instead and references to "${assignedName}" were left alone.`,
|
|
246678
|
+
{
|
|
246679
|
+
place: node.position,
|
|
246680
|
+
ruleId: "assign-names/invalid-name",
|
|
246681
|
+
source: "v06-to-v07"
|
|
246682
|
+
}
|
|
246683
|
+
);
|
|
246684
|
+
deleteAssignNames(node);
|
|
246685
|
+
setCompositeName(node, context.uniqueName("copy"));
|
|
246686
|
+
return;
|
|
246687
|
+
}
|
|
246688
|
+
if (existingName) {
|
|
246689
|
+
if (existingName !== assignedName && !nameBelongsToAnother) {
|
|
246690
|
+
context.registry.register(
|
|
246691
|
+
assignedName,
|
|
246692
|
+
[makeIndexedPathPart(existingName, [])],
|
|
246693
|
+
origin,
|
|
246694
|
+
file
|
|
246695
|
+
);
|
|
246696
|
+
} else if (existingName !== assignedName) {
|
|
246697
|
+
reportUnusableName(node, assignedName, nameIsUnusable, file);
|
|
246698
|
+
}
|
|
246699
|
+
deleteAssignNames(node);
|
|
246700
|
+
setCompositeName(node, existingName);
|
|
246701
|
+
return;
|
|
246702
|
+
}
|
|
246703
|
+
if (nameIsTaken) {
|
|
246704
|
+
const generated = context.uniqueName("copy");
|
|
246705
|
+
if (nameBelongsToAnother) {
|
|
246706
|
+
reportUnusableName(node, assignedName, nameIsUnusable, file);
|
|
246707
|
+
} else {
|
|
246708
|
+
context.registry.register(
|
|
246709
|
+
assignedName,
|
|
246710
|
+
[makeIndexedPathPart(generated, [])],
|
|
246711
|
+
origin,
|
|
246712
|
+
file
|
|
246713
|
+
);
|
|
246714
|
+
}
|
|
246715
|
+
deleteAssignNames(node);
|
|
246716
|
+
setCompositeName(node, generated);
|
|
246717
|
+
return;
|
|
246718
|
+
}
|
|
246719
|
+
context.claimedNames.add(assignedName);
|
|
246720
|
+
context.registry.register(assignedName, void 0, origin, file);
|
|
246721
|
+
setCompositeName(node, assignedName);
|
|
246722
|
+
}
|
|
246723
|
+
function warnAboutExternalRef(node, file) {
|
|
246724
|
+
const toKey = findKey$1(node, "to");
|
|
246725
|
+
if (!toKey) {
|
|
246726
|
+
return;
|
|
246727
|
+
}
|
|
246728
|
+
const target = toXml(node.attributes[toKey].children).trim();
|
|
246729
|
+
if (!/^doenet:.*(activityId|doenetId|cid|pageId)=/i.test(target)) {
|
|
246730
|
+
return;
|
|
246731
|
+
}
|
|
246732
|
+
file.message(
|
|
246733
|
+
`<ref to="${target}"> points at a v0.6 activity; replace the identifier with the v0.7 content id (or a doenet.org URL) of the converted document.`,
|
|
246734
|
+
{
|
|
246735
|
+
place: node.position,
|
|
246736
|
+
ruleId: "external-ref/needs-new-content-id",
|
|
246737
|
+
source: "v06-to-v07"
|
|
246738
|
+
}
|
|
246739
|
+
);
|
|
246740
|
+
}
|
|
246741
|
+
function reportUnusableName(node, assignedName, nameIsUnusable, file) {
|
|
246742
|
+
if (nameIsUnusable) {
|
|
246743
|
+
file.message(
|
|
246744
|
+
`<${node.name}> assigns the name "${assignedName}", which v0.7 cannot use as a name — it must start with a letter and hold only letters, digits, underscores and hyphens. A generated name was used instead and references to "${assignedName}" were left alone.`,
|
|
246745
|
+
{
|
|
246746
|
+
place: node.position,
|
|
246747
|
+
ruleId: "assign-names/invalid-name",
|
|
246748
|
+
source: "v06-to-v07"
|
|
246749
|
+
}
|
|
246750
|
+
);
|
|
246751
|
+
return;
|
|
246752
|
+
}
|
|
246753
|
+
file.message(
|
|
246754
|
+
`<${node.name}> assigns the name "${assignedName}", but something else in the document is already called that. References to "${assignedName}" were left pointing where they already pointed.`,
|
|
246755
|
+
{
|
|
246756
|
+
place: node.position,
|
|
246757
|
+
ruleId: "copy/name-already-taken",
|
|
246758
|
+
source: "v06-to-v07"
|
|
246759
|
+
}
|
|
246760
|
+
);
|
|
246761
|
+
}
|
|
246762
|
+
function findKey$1(node, attrName) {
|
|
246763
|
+
return Object.keys(node.attributes).find(
|
|
246764
|
+
(key) => key.toLowerCase() === attrName.toLowerCase()
|
|
246765
|
+
);
|
|
246766
|
+
}
|
|
246767
|
+
const upgradeAttributeSyntax = (context) => {
|
|
244833
246768
|
return async (tree, file) => {
|
|
244834
|
-
visit(tree, (node) => {
|
|
246769
|
+
visit(tree, (node, info) => {
|
|
244835
246770
|
if (node.type !== "macro") {
|
|
244836
246771
|
return;
|
|
244837
246772
|
}
|
|
@@ -244857,6 +246792,18 @@ const upgradeAttributeSyntax = () => {
|
|
|
244857
246792
|
}
|
|
244858
246793
|
});
|
|
244859
246794
|
Object.assign(node, copy2);
|
|
246795
|
+
const assigned = readAssignNames(node);
|
|
246796
|
+
convertAssignNames(
|
|
246797
|
+
node,
|
|
246798
|
+
namespaceChainOf(info.parents, context),
|
|
246799
|
+
context,
|
|
246800
|
+
file
|
|
246801
|
+
);
|
|
246802
|
+
warnIfNameCouldNotBeKept(
|
|
246803
|
+
node,
|
|
246804
|
+
assigned,
|
|
246805
|
+
file
|
|
246806
|
+
);
|
|
244860
246807
|
});
|
|
244861
246808
|
const macrosInAttributes = [];
|
|
244862
246809
|
visit(tree, (node, info) => {
|
|
@@ -244950,39 +246897,34 @@ const upgradeAttributeSyntax = () => {
|
|
|
244950
246897
|
documentElement.children.unshift(setupTag);
|
|
244951
246898
|
};
|
|
244952
246899
|
};
|
|
244953
|
-
|
|
246900
|
+
function warnIfNameCouldNotBeKept(node, assigned, file) {
|
|
246901
|
+
if (!assigned) {
|
|
246902
|
+
return;
|
|
246903
|
+
}
|
|
246904
|
+
const name2 = toXml(node.attributes["name"]?.children ?? []).trim();
|
|
246905
|
+
if (name2 === assigned.trim()) {
|
|
246906
|
+
return;
|
|
246907
|
+
}
|
|
246908
|
+
file.message(
|
|
246909
|
+
`A reference wrote assignNames="${assigned}" in its attributes, and that name could not be kept${name2 ? ` (the element is now named "${name2}")` : ""}. References to "${assigned}" need fixing by hand.`,
|
|
246910
|
+
{
|
|
246911
|
+
place: node.position,
|
|
246912
|
+
ruleId: "assign-names/late-attribute-assignment",
|
|
246913
|
+
source: "v06-to-v07"
|
|
246914
|
+
}
|
|
246915
|
+
);
|
|
246916
|
+
}
|
|
246917
|
+
const upgradeMapElement = (context) => {
|
|
244954
246918
|
return (tree, file) => {
|
|
244955
|
-
|
|
244956
|
-
replaceNode(tree, (node) => {
|
|
246919
|
+
replaceNode(tree, (node, info) => {
|
|
244957
246920
|
if (!isDastElement(node)) {
|
|
244958
246921
|
return;
|
|
244959
246922
|
}
|
|
244960
|
-
if (node.name !== "map") {
|
|
246923
|
+
if (node.name.toLowerCase() !== "map") {
|
|
244961
246924
|
return;
|
|
244962
246925
|
}
|
|
244963
|
-
|
|
244964
|
-
const
|
|
244965
|
-
if (name2 && assignNames.length > 0) {
|
|
244966
|
-
assignNames.forEach((assignName, index2) => {
|
|
244967
|
-
refsToRename[assignName] = [
|
|
244968
|
-
{
|
|
244969
|
-
type: "pathPart",
|
|
244970
|
-
name: name2,
|
|
244971
|
-
index: [
|
|
244972
|
-
{
|
|
244973
|
-
type: "index",
|
|
244974
|
-
value: [
|
|
244975
|
-
{
|
|
244976
|
-
type: "text",
|
|
244977
|
-
value: `${index2 + 1}`
|
|
244978
|
-
}
|
|
244979
|
-
]
|
|
244980
|
-
}
|
|
244981
|
-
]
|
|
244982
|
-
}
|
|
244983
|
-
];
|
|
244984
|
-
});
|
|
244985
|
-
}
|
|
246926
|
+
let name2 = toXml(node.attributes["name"]?.children).trim();
|
|
246927
|
+
const assignNamesValue = readAssignNames(node);
|
|
244986
246928
|
const templateNode = node.children.find(
|
|
244987
246929
|
(child) => isDastElement(child) && child.name === "template"
|
|
244988
246930
|
);
|
|
@@ -244992,10 +246934,32 @@ const upgradeMapElement = () => {
|
|
|
244992
246934
|
if (!templateNode || !isDastElement(templateNode) || !sourcesNode || !isDastElement(sourcesNode)) {
|
|
244993
246935
|
file.message(
|
|
244994
246936
|
`Map element must have both a template and sources children to be converted`,
|
|
244995
|
-
{
|
|
246937
|
+
{
|
|
246938
|
+
place: node.position,
|
|
246939
|
+
ruleId: "map/missing-template-or-sources",
|
|
246940
|
+
source: "v06-to-v07"
|
|
246941
|
+
}
|
|
244996
246942
|
);
|
|
244997
246943
|
return;
|
|
244998
246944
|
}
|
|
246945
|
+
if (assignNamesValue) {
|
|
246946
|
+
name2 = registerCompositeAssignNames({
|
|
246947
|
+
node,
|
|
246948
|
+
assignNamesValue,
|
|
246949
|
+
fallbackBase: "repeat",
|
|
246950
|
+
ancestorNames: namespaceChainOf(info.parents, context),
|
|
246951
|
+
context,
|
|
246952
|
+
file,
|
|
246953
|
+
// A nested name such as `assignNames="(a b)"` counts positions
|
|
246954
|
+
// inside one iteration, which are the template's children — and
|
|
246955
|
+
// v0.6 skipped bare text there while v0.7 counts it.
|
|
246956
|
+
positionMap: makeTemplatePositionMap(
|
|
246957
|
+
templateNode,
|
|
246958
|
+
node,
|
|
246959
|
+
file
|
|
246960
|
+
)
|
|
246961
|
+
}) ?? name2;
|
|
246962
|
+
}
|
|
244999
246963
|
const valueName = toXml(
|
|
245000
246964
|
sourcesNode.attributes["alias"]?.children
|
|
245001
246965
|
).trim();
|
|
@@ -245007,6 +246971,7 @@ const upgradeMapElement = () => {
|
|
|
245007
246971
|
(child) => isDastElement(child) && child.name === "sequence"
|
|
245008
246972
|
)) {
|
|
245009
246973
|
if (name2) {
|
|
246974
|
+
warnIfNameLost(sequenceNode, name2, file);
|
|
245010
246975
|
sequenceNode.attributes["name"] = {
|
|
245011
246976
|
type: "attribute",
|
|
245012
246977
|
name: "name",
|
|
@@ -245014,6 +246979,7 @@ const upgradeMapElement = () => {
|
|
|
245014
246979
|
};
|
|
245015
246980
|
}
|
|
245016
246981
|
sequenceNode.name = "repeatForSequence";
|
|
246982
|
+
inheritNamespace(templateNode, sequenceNode, context);
|
|
245017
246983
|
if (valueName) {
|
|
245018
246984
|
sequenceNode.attributes["valueName"] = {
|
|
245019
246985
|
type: "attribute",
|
|
@@ -245043,12 +247009,15 @@ const upgradeMapElement = () => {
|
|
|
245043
247009
|
attributes: {},
|
|
245044
247010
|
children: [groupTag]
|
|
245045
247011
|
};
|
|
245046
|
-
const groupName =
|
|
247012
|
+
const groupName = context.uniqueName("group");
|
|
245047
247013
|
groupTag.attributes["name"] = {
|
|
245048
247014
|
type: "attribute",
|
|
245049
247015
|
name: "name",
|
|
245050
247016
|
children: reparseAttribute(groupName)
|
|
245051
247017
|
};
|
|
247018
|
+
if (name2) {
|
|
247019
|
+
warnIfNameLost(templateNode, name2, file);
|
|
247020
|
+
}
|
|
245052
247021
|
templateNode.name = "repeat";
|
|
245053
247022
|
templateNode.attributes["for"] = {
|
|
245054
247023
|
type: "attribute",
|
|
@@ -245079,19 +247048,22 @@ const upgradeMapElement = () => {
|
|
|
245079
247048
|
return [setupTag, templateNode];
|
|
245080
247049
|
}
|
|
245081
247050
|
});
|
|
245082
|
-
visit(tree, (node) => {
|
|
245083
|
-
if (node.type !== "macro") {
|
|
245084
|
-
return;
|
|
245085
|
-
}
|
|
245086
|
-
if (!node.path.some((part) => refsToRename[part.name])) {
|
|
245087
|
-
return;
|
|
245088
|
-
}
|
|
245089
|
-
node.path = node.path.flatMap((part) => {
|
|
245090
|
-
return refsToRename[part.name] || [part];
|
|
245091
|
-
});
|
|
245092
|
-
});
|
|
245093
247051
|
};
|
|
245094
247052
|
};
|
|
247053
|
+
function warnIfNameLost(node, newName, file) {
|
|
247054
|
+
const existing = toXml(node.attributes["name"]?.children).trim();
|
|
247055
|
+
if (!existing || existing === newName) {
|
|
247056
|
+
return;
|
|
247057
|
+
}
|
|
247058
|
+
file.message(
|
|
247059
|
+
`<${node.name} name="${existing}"> becomes the <repeat> that takes the name "${newName}", so "${existing}" is gone; references to it need fixing by hand.`,
|
|
247060
|
+
{
|
|
247061
|
+
place: node.position,
|
|
247062
|
+
ruleId: "map/name-overwritten",
|
|
247063
|
+
source: "v06-to-v07"
|
|
247064
|
+
}
|
|
247065
|
+
);
|
|
247066
|
+
}
|
|
245095
247067
|
const upgradeModuleElement = () => {
|
|
245096
247068
|
return (tree, file) => {
|
|
245097
247069
|
visit(tree, (node) => {
|
|
@@ -245107,14 +247079,30 @@ const upgradeModuleElement = () => {
|
|
|
245107
247079
|
if (!setupNode) {
|
|
245108
247080
|
return;
|
|
245109
247081
|
}
|
|
245110
|
-
|
|
245111
|
-
(child) => !isDastElement(child) || child.name !== "setup"
|
|
245112
|
-
);
|
|
247082
|
+
const isCustomAttribute = (child) => isDastElement(child) && child.name === "customAttribute";
|
|
245113
247083
|
const customAttributeNodes = setupNode.children.filter(
|
|
245114
|
-
(child) =>
|
|
247084
|
+
(child) => isCustomAttribute(child)
|
|
247085
|
+
);
|
|
247086
|
+
const otherSetupChildren = setupNode.children.filter(
|
|
247087
|
+
(child) => !isCustomAttribute(child)
|
|
245115
247088
|
);
|
|
245116
247089
|
setupNode.children = [];
|
|
245117
247090
|
setupNode.name = "moduleAttributes";
|
|
247091
|
+
if (otherSetupChildren.some(
|
|
247092
|
+
(child) => child.type !== "text" || child.value.trim() !== ""
|
|
247093
|
+
)) {
|
|
247094
|
+
const newSetup = {
|
|
247095
|
+
type: "element",
|
|
247096
|
+
name: "setup",
|
|
247097
|
+
attributes: {},
|
|
247098
|
+
children: otherSetupChildren
|
|
247099
|
+
};
|
|
247100
|
+
node.children.splice(
|
|
247101
|
+
node.children.indexOf(setupNode) + 1,
|
|
247102
|
+
0,
|
|
247103
|
+
newSetup
|
|
247104
|
+
);
|
|
247105
|
+
}
|
|
245118
247106
|
for (const customAttributeNode of customAttributeNodes) {
|
|
245119
247107
|
const name2 = toXml(
|
|
245120
247108
|
customAttributeNode.attributes["assignNames"]?.children
|
|
@@ -245126,7 +247114,9 @@ const upgradeModuleElement = () => {
|
|
|
245126
247114
|
file.message(
|
|
245127
247115
|
`Module "customAttribute" is missing a "componentType"; the syntax cannot be upgraded with a "componentType".`,
|
|
245128
247116
|
{
|
|
245129
|
-
place: customAttributeNode.position
|
|
247117
|
+
place: customAttributeNode.position,
|
|
247118
|
+
ruleId: "module/custom-attribute-without-type",
|
|
247119
|
+
source: "v06-to-v07"
|
|
245130
247120
|
}
|
|
245131
247121
|
);
|
|
245132
247122
|
continue;
|
|
@@ -245189,10 +247179,459 @@ const removeDefinitionsElement = () => {
|
|
|
245189
247179
|
});
|
|
245190
247180
|
};
|
|
245191
247181
|
};
|
|
247182
|
+
const GLOBAL_RULES = {
|
|
247183
|
+
// v0.6 renamed `targets*` to `sources*`; v0.7 renamed them again to `references*`.
|
|
247184
|
+
sourcesareresponses: {
|
|
247185
|
+
kind: "renameToReferenceList",
|
|
247186
|
+
to: "referencesAreResponses"
|
|
247187
|
+
},
|
|
247188
|
+
targetsareresponses: {
|
|
247189
|
+
kind: "renameToReferenceList",
|
|
247190
|
+
to: "referencesAreResponses"
|
|
247191
|
+
},
|
|
247192
|
+
sourcesarefunctionsymbols: {
|
|
247193
|
+
kind: "renameToReferenceList",
|
|
247194
|
+
to: "referencesAreFunctionSymbols"
|
|
247195
|
+
},
|
|
247196
|
+
targetsarefunctionsymbols: {
|
|
247197
|
+
kind: "renameToReferenceList",
|
|
247198
|
+
to: "referencesAreFunctionSymbols"
|
|
247199
|
+
},
|
|
247200
|
+
triggerwithtargets: { kind: "renameToReferenceList", to: "triggerWith" },
|
|
247201
|
+
triggerwithtnames: { kind: "renameToReferenceList", to: "triggerWith" },
|
|
247202
|
+
updatewithtname: { kind: "rename", to: "updateWith" },
|
|
247203
|
+
updatewithtarget: { kind: "rename", to: "updateWith" },
|
|
247204
|
+
// `tname` was the pre-v0.6 spelling of the attribute naming what to act on. On
|
|
247205
|
+
// components that *copy*, it became `source`; everywhere else it became `target`.
|
|
247206
|
+
// The element-specific tables below override this for the copying components.
|
|
247207
|
+
tname: { kind: "rename", to: "target" }
|
|
247208
|
+
};
|
|
247209
|
+
const ELEMENT_RULES = {
|
|
247210
|
+
copy: { tname: { kind: "rename", to: "source" } },
|
|
247211
|
+
collect: {
|
|
247212
|
+
tname: { kind: "rename", to: "source" },
|
|
247213
|
+
draggable: {
|
|
247214
|
+
kind: "remove",
|
|
247215
|
+
because: "<collect> does not take `draggable` in v0.7"
|
|
247216
|
+
}
|
|
247217
|
+
},
|
|
247218
|
+
extract: { tname: { kind: "rename", to: "source" } },
|
|
247219
|
+
updatevalue: {
|
|
247220
|
+
// `<updateValue target="x" prop="y">` is a single path in v0.7.
|
|
247221
|
+
prop: { kind: "mergeInto", target: "target" }
|
|
247222
|
+
},
|
|
247223
|
+
animatefromsequence: { prop: { kind: "mergeInto", target: "target" } },
|
|
247224
|
+
ref: {
|
|
247225
|
+
page: {
|
|
247226
|
+
kind: "remove",
|
|
247227
|
+
because: "v0.7 documents are not paginated, so <ref> has no `page`; link to the individual document instead",
|
|
247228
|
+
ruleId: "deprecated/ref-page"
|
|
247229
|
+
}
|
|
247230
|
+
},
|
|
247231
|
+
variantcontrol: { nvariants: { kind: "rename", to: "numVariants" } },
|
|
247232
|
+
option: {
|
|
247233
|
+
selectforvariantnames: { kind: "rename", to: "selectForVariants" }
|
|
247234
|
+
},
|
|
247235
|
+
graph: {
|
|
247236
|
+
xlabel: { kind: "toChildElement", elementName: "xLabel" },
|
|
247237
|
+
ylabel: { kind: "toChildElement", elementName: "yLabel" },
|
|
247238
|
+
height: {
|
|
247239
|
+
kind: "remove",
|
|
247240
|
+
because: "v0.7 graphs are sized with `size`, `width` and `aspectRatio`"
|
|
247241
|
+
}
|
|
247242
|
+
},
|
|
247243
|
+
image: {
|
|
247244
|
+
// v0.6 rendered `description` as the image's `alt` text. v0.7 carries that in a
|
|
247245
|
+
// `<shortDescription>` child, which the reference pages describe as required for
|
|
247246
|
+
// accessibility, so this must be converted rather than dropped.
|
|
247247
|
+
description: {
|
|
247248
|
+
kind: "toChildElement",
|
|
247249
|
+
elementName: "shortDescription"
|
|
247250
|
+
}
|
|
247251
|
+
},
|
|
247252
|
+
conditionalcontent: {
|
|
247253
|
+
maximumnumbertoshow: {
|
|
247254
|
+
kind: "remove",
|
|
247255
|
+
because: "v0.7 <conditionalContent> always shows one case"
|
|
247256
|
+
}
|
|
247257
|
+
},
|
|
247258
|
+
embed: {
|
|
247259
|
+
encodedgeogebracontent: {
|
|
247260
|
+
kind: "unsupported",
|
|
247261
|
+
because: "v0.7 has no <embed> element and no GeoGebra support; this content must be replaced by hand",
|
|
247262
|
+
ruleId: "no-v07-equivalent/embed-geogebra"
|
|
247263
|
+
}
|
|
247264
|
+
},
|
|
247265
|
+
point: {
|
|
247266
|
+
link: {
|
|
247267
|
+
kind: "remove",
|
|
247268
|
+
because: "`link` only means something alongside `copySource`",
|
|
247269
|
+
// `copySourceToExtendOrCopy` needs `link` when there is a `copySource`.
|
|
247270
|
+
when: (_value, node) => !findAttrKey(node, "copySource") && !findAttrKey(node, "source")
|
|
247271
|
+
}
|
|
247272
|
+
},
|
|
247273
|
+
tabular: removeSides(),
|
|
247274
|
+
row: removeSides(),
|
|
247275
|
+
cell: removeSides()
|
|
247276
|
+
};
|
|
247277
|
+
function removeSides() {
|
|
247278
|
+
const rule = {
|
|
247279
|
+
kind: "remove",
|
|
247280
|
+
because: "v0.7 tables do not take per-side border attributes"
|
|
247281
|
+
};
|
|
247282
|
+
return { top: rule, bottom: rule, left: rule, right: rule };
|
|
247283
|
+
}
|
|
247284
|
+
const upgradeDeprecatedAttributes = () => {
|
|
247285
|
+
return (tree, file) => {
|
|
247286
|
+
visit(tree, (node) => {
|
|
247287
|
+
if (!isDastElement(node)) {
|
|
247288
|
+
return;
|
|
247289
|
+
}
|
|
247290
|
+
const elm = node;
|
|
247291
|
+
const rules = {
|
|
247292
|
+
...GLOBAL_RULES,
|
|
247293
|
+
...ELEMENT_RULES[elm.name.toLowerCase()] || {}
|
|
247294
|
+
};
|
|
247295
|
+
const keys2 = Object.keys(elm.attributes);
|
|
247296
|
+
const isMerge = (key) => rules[key.toLowerCase()]?.kind === "mergeInto";
|
|
247297
|
+
for (const key of [
|
|
247298
|
+
...keys2.filter((k2) => !isMerge(k2)),
|
|
247299
|
+
...keys2.filter(isMerge)
|
|
247300
|
+
]) {
|
|
247301
|
+
const rule = rules[key.toLowerCase()];
|
|
247302
|
+
if (!rule) {
|
|
247303
|
+
continue;
|
|
247304
|
+
}
|
|
247305
|
+
const attr = elm.attributes[key];
|
|
247306
|
+
if (!attr) {
|
|
247307
|
+
continue;
|
|
247308
|
+
}
|
|
247309
|
+
const value = toXml(attr.children).trim();
|
|
247310
|
+
if (rule.when && !rule.when(value, elm)) {
|
|
247311
|
+
continue;
|
|
247312
|
+
}
|
|
247313
|
+
applyRule(elm, key, attr, value, rule, file);
|
|
247314
|
+
}
|
|
247315
|
+
});
|
|
247316
|
+
};
|
|
247317
|
+
};
|
|
247318
|
+
const prependedChildCount = /* @__PURE__ */ new WeakMap();
|
|
247319
|
+
function applyRule(elm, key, attr, value, rule, file) {
|
|
247320
|
+
switch (rule.kind) {
|
|
247321
|
+
case "rename": {
|
|
247322
|
+
renameAttrInPlace(elm, key, rule.to);
|
|
247323
|
+
return;
|
|
247324
|
+
}
|
|
247325
|
+
case "renameToReferenceList": {
|
|
247326
|
+
renameAttrInPlace(elm, key, rule.to);
|
|
247327
|
+
const dollared = value.split(/\s+/).filter((t4) => t4).map(referenceTo).join(" ");
|
|
247328
|
+
elm.attributes[rule.to].children = reparseAttributeV6(dollared);
|
|
247329
|
+
return;
|
|
247330
|
+
}
|
|
247331
|
+
case "mergeInto": {
|
|
247332
|
+
const targetKey = findAttrKey(elm, rule.target);
|
|
247333
|
+
if (!targetKey || !value) {
|
|
247334
|
+
return;
|
|
247335
|
+
}
|
|
247336
|
+
const targetValue = toXml(
|
|
247337
|
+
elm.attributes[targetKey].children
|
|
247338
|
+
).trim();
|
|
247339
|
+
elm.attributes[targetKey].children = reparseAttributeV6(
|
|
247340
|
+
`${targetValue}.${value}`
|
|
247341
|
+
);
|
|
247342
|
+
delete elm.attributes[key];
|
|
247343
|
+
return;
|
|
247344
|
+
}
|
|
247345
|
+
case "toChildElement": {
|
|
247346
|
+
if (!value) {
|
|
247347
|
+
delete elm.attributes[key];
|
|
247348
|
+
return;
|
|
247349
|
+
}
|
|
247350
|
+
const child = {
|
|
247351
|
+
type: "element",
|
|
247352
|
+
name: rule.elementName,
|
|
247353
|
+
attributes: {},
|
|
247354
|
+
children: attr.children,
|
|
247355
|
+
position: attr.position
|
|
247356
|
+
};
|
|
247357
|
+
const alreadyPrepended = prependedChildCount.get(elm) || 0;
|
|
247358
|
+
elm.children.splice(alreadyPrepended, 0, child);
|
|
247359
|
+
prependedChildCount.set(elm, alreadyPrepended + 1);
|
|
247360
|
+
delete elm.attributes[key];
|
|
247361
|
+
return;
|
|
247362
|
+
}
|
|
247363
|
+
case "remove": {
|
|
247364
|
+
delete elm.attributes[key];
|
|
247365
|
+
file.message(
|
|
247366
|
+
`Removed ${key}="${value}" from <${elm.name}>: ${rule.because}.`,
|
|
247367
|
+
{
|
|
247368
|
+
place: elm.position,
|
|
247369
|
+
ruleId: rule.ruleId || `deprecated/${key.toLowerCase()}`,
|
|
247370
|
+
source: "v06-to-v07"
|
|
247371
|
+
}
|
|
247372
|
+
);
|
|
247373
|
+
return;
|
|
247374
|
+
}
|
|
247375
|
+
case "unsupported": {
|
|
247376
|
+
file.message(
|
|
247377
|
+
`<${elm.name} ${key}="..."> was left unchanged: ${rule.because}.`,
|
|
247378
|
+
{
|
|
247379
|
+
place: elm.position,
|
|
247380
|
+
ruleId: rule.ruleId || `no-v07-equivalent/${key.toLowerCase()}`,
|
|
247381
|
+
source: "v06-to-v07"
|
|
247382
|
+
}
|
|
247383
|
+
);
|
|
247384
|
+
return;
|
|
247385
|
+
}
|
|
247386
|
+
}
|
|
247387
|
+
}
|
|
247388
|
+
function findAttrKey(elm, attrName) {
|
|
247389
|
+
return Object.keys(elm.attributes).find(
|
|
247390
|
+
(key) => key.toLowerCase() === attrName.toLowerCase()
|
|
247391
|
+
);
|
|
247392
|
+
}
|
|
247393
|
+
function referenceTo(token) {
|
|
247394
|
+
if (token.startsWith("$(")) {
|
|
247395
|
+
return token;
|
|
247396
|
+
}
|
|
247397
|
+
const name2 = token.startsWith("$") ? token.slice(1) : token;
|
|
247398
|
+
return /^[a-zA-Z0-9_]+$/.test(name2) ? `$${name2}` : `$(${name2})`;
|
|
247399
|
+
}
|
|
247400
|
+
const ALL_ITEMS_PROPS = /* @__PURE__ */ new Set(["maths", "texts", "numbers", "booleans"]);
|
|
247401
|
+
const NUMBERED_ITEM_PROP = /^(math|text|number|boolean)(\d+)$/;
|
|
247402
|
+
const ELEMENTS_WITH_PROP = /* @__PURE__ */ new Set(["copy", "extract"]);
|
|
247403
|
+
const upgradeListProps = () => {
|
|
247404
|
+
return (tree, file) => {
|
|
247405
|
+
visitAllMacros(tree, (node) => {
|
|
247406
|
+
node.path = rewritePath(node.path);
|
|
247407
|
+
});
|
|
247408
|
+
visitAll(tree, (node) => {
|
|
247409
|
+
if (!isDastElement(node) || !ELEMENTS_WITH_PROP.has(node.name.toLowerCase())) {
|
|
247410
|
+
return;
|
|
247411
|
+
}
|
|
247412
|
+
rewritePropAttribute(node, file);
|
|
247413
|
+
rewriteRawSourceAttribute(node);
|
|
247414
|
+
});
|
|
247415
|
+
};
|
|
247416
|
+
};
|
|
247417
|
+
const ELEMENTS_WITH_RAW_SOURCE = /* @__PURE__ */ new Set(["copy", "extract"]);
|
|
247418
|
+
function rewriteRawSourceAttribute(node) {
|
|
247419
|
+
if (!ELEMENTS_WITH_RAW_SOURCE.has(node.name.toLowerCase())) {
|
|
247420
|
+
return;
|
|
247421
|
+
}
|
|
247422
|
+
const sourceKey = findKey(node, "source");
|
|
247423
|
+
if (!sourceKey) {
|
|
247424
|
+
return;
|
|
247425
|
+
}
|
|
247426
|
+
const attr = node.attributes[sourceKey];
|
|
247427
|
+
if (attr.children.length !== 1 || attr.children[0].type !== "text") {
|
|
247428
|
+
return;
|
|
247429
|
+
}
|
|
247430
|
+
const child = attr.children[0];
|
|
247431
|
+
const value = child.value.trim();
|
|
247432
|
+
if (!value || value.startsWith("$") || !value.includes(".")) {
|
|
247433
|
+
return;
|
|
247434
|
+
}
|
|
247435
|
+
let path;
|
|
247436
|
+
try {
|
|
247437
|
+
path = parseReferencePath(value);
|
|
247438
|
+
} catch {
|
|
247439
|
+
return;
|
|
247440
|
+
}
|
|
247441
|
+
restoreRawPropPositions(node, sourceKey, path);
|
|
247442
|
+
const rewritten = rewritePath(path);
|
|
247443
|
+
if (rewritten === path) {
|
|
247444
|
+
return;
|
|
247445
|
+
}
|
|
247446
|
+
child.value = toXml(rewritten);
|
|
247447
|
+
}
|
|
247448
|
+
function rewritePath(path) {
|
|
247449
|
+
if (path.length < 2) {
|
|
247450
|
+
return path;
|
|
247451
|
+
}
|
|
247452
|
+
const result2 = [path[0]];
|
|
247453
|
+
for (const part of path.slice(1)) {
|
|
247454
|
+
const previous = result2[result2.length - 1];
|
|
247455
|
+
if (!isPropAccess(part)) {
|
|
247456
|
+
result2.push(part);
|
|
247457
|
+
continue;
|
|
247458
|
+
}
|
|
247459
|
+
if (ALL_ITEMS_PROPS.has(part.name)) {
|
|
247460
|
+
previous.index = [...previous.index, ...part.index];
|
|
247461
|
+
continue;
|
|
247462
|
+
}
|
|
247463
|
+
const numbered = NUMBERED_ITEM_PROP.exec(part.name);
|
|
247464
|
+
if (numbered) {
|
|
247465
|
+
previous.index = [
|
|
247466
|
+
...previous.index,
|
|
247467
|
+
{
|
|
247468
|
+
type: "index",
|
|
247469
|
+
value: [{ type: "text", value: numbered[2] }]
|
|
247470
|
+
},
|
|
247471
|
+
...part.index
|
|
247472
|
+
];
|
|
247473
|
+
continue;
|
|
247474
|
+
}
|
|
247475
|
+
result2.push(part);
|
|
247476
|
+
}
|
|
247477
|
+
return result2;
|
|
247478
|
+
}
|
|
247479
|
+
function rewritePropAttribute(node, file) {
|
|
247480
|
+
const propKey = findKey(node, "prop");
|
|
247481
|
+
if (!propKey) {
|
|
247482
|
+
return;
|
|
247483
|
+
}
|
|
247484
|
+
const prop = toXml(node.attributes[propKey].children).trim();
|
|
247485
|
+
const numbered = NUMBERED_ITEM_PROP.exec(prop);
|
|
247486
|
+
if (!ALL_ITEMS_PROPS.has(prop) && !numbered) {
|
|
247487
|
+
return;
|
|
247488
|
+
}
|
|
247489
|
+
const sourceKey = findKey(node, "source");
|
|
247490
|
+
if (!sourceKey) {
|
|
247491
|
+
return;
|
|
247492
|
+
}
|
|
247493
|
+
const sourceAttr = node.attributes[sourceKey];
|
|
247494
|
+
const source = toXml(sourceAttr.children).trim();
|
|
247495
|
+
if (!source) {
|
|
247496
|
+
return;
|
|
247497
|
+
}
|
|
247498
|
+
if (!numbered) {
|
|
247499
|
+
if (node.name.toLowerCase() === "extract") {
|
|
247500
|
+
file.message(
|
|
247501
|
+
`<extract prop="${prop}"> has no v0.7 equivalent and was left as it is; the prop names the whole list, which v0.7 reaches as $${source}.`,
|
|
247502
|
+
{
|
|
247503
|
+
place: node.position,
|
|
247504
|
+
ruleId: "no-v07-equivalent/extract",
|
|
247505
|
+
source: "v06-to-v07"
|
|
247506
|
+
}
|
|
247507
|
+
);
|
|
247508
|
+
return;
|
|
247509
|
+
}
|
|
247510
|
+
delete node.attributes[propKey];
|
|
247511
|
+
return;
|
|
247512
|
+
}
|
|
247513
|
+
const hadDollar = source.startsWith("$");
|
|
247514
|
+
let path;
|
|
247515
|
+
try {
|
|
247516
|
+
path = parseReferencePath(hadDollar ? source.slice(1) : source);
|
|
247517
|
+
} catch (e4) {
|
|
247518
|
+
return;
|
|
247519
|
+
}
|
|
247520
|
+
const last2 = path[path.length - 1];
|
|
247521
|
+
last2.index = [
|
|
247522
|
+
...last2.index,
|
|
247523
|
+
{ type: "index", value: [{ type: "text", value: numbered[2] }] }
|
|
247524
|
+
];
|
|
247525
|
+
sourceAttr.children = hadDollar ? [{ type: "macro", path, attributes: {} }] : [{ type: "text", value: toXml(path) }];
|
|
247526
|
+
delete node.attributes[propKey];
|
|
247527
|
+
}
|
|
247528
|
+
function findKey(node, attrName) {
|
|
247529
|
+
return Object.keys(node.attributes).find(
|
|
247530
|
+
(key) => key.toLowerCase() === attrName.toLowerCase()
|
|
247531
|
+
);
|
|
247532
|
+
}
|
|
247533
|
+
const upgradeAssignNames = (context) => {
|
|
247534
|
+
return (tree, file) => {
|
|
247535
|
+
visitAll(tree, (node, parents) => {
|
|
247536
|
+
if (!isDastElement(node)) {
|
|
247537
|
+
return;
|
|
247538
|
+
}
|
|
247539
|
+
const spec = lookupComposite(node);
|
|
247540
|
+
if (!spec) {
|
|
247541
|
+
return;
|
|
247542
|
+
}
|
|
247543
|
+
const assignNamesValue = readAssignNames(node);
|
|
247544
|
+
if (assignNamesValue === void 0) {
|
|
247545
|
+
return;
|
|
247546
|
+
}
|
|
247547
|
+
if (spec.noV07Equivalent) {
|
|
247548
|
+
file.message(
|
|
247549
|
+
`<${node.name}> has no v0.7 equivalent and must be rewritten by hand; its references were converted as if it behaved like a v0.6 composite.`,
|
|
247550
|
+
{
|
|
247551
|
+
place: node.position,
|
|
247552
|
+
ruleId: `no-v07-equivalent/${spec.name.toLowerCase()}`,
|
|
247553
|
+
source: "v06-to-v07"
|
|
247554
|
+
}
|
|
247555
|
+
);
|
|
247556
|
+
}
|
|
247557
|
+
if (!assignNamesValue) {
|
|
247558
|
+
deleteAssignNames(node);
|
|
247559
|
+
return;
|
|
247560
|
+
}
|
|
247561
|
+
const parsed = breakStringInPiecesBySpacesOrParens(assignNamesValue);
|
|
247562
|
+
if (parsed.success && firstLeafName(parsed.pieces) === void 0) {
|
|
247563
|
+
deleteAssignNames(node);
|
|
247564
|
+
return;
|
|
247565
|
+
}
|
|
247566
|
+
const onlyPiece = parsed.success && parsed.pieces.length === 1 && typeof parsed.pieces[0] === "string" ? parsed.pieces[0] : void 0;
|
|
247567
|
+
if (onlyPiece !== void 0 && isValidReferenceableName(onlyPiece) && !findAttribute(node, "name") && !context.existingNames.has(onlyPiece) && !context.claimedNames.has(onlyPiece) && producesSingleReplacement(node, spec)) {
|
|
247568
|
+
context.claimedNames.add(onlyPiece);
|
|
247569
|
+
context.registry.register(
|
|
247570
|
+
onlyPiece,
|
|
247571
|
+
void 0,
|
|
247572
|
+
{
|
|
247573
|
+
elementName: node.name,
|
|
247574
|
+
position: node.position,
|
|
247575
|
+
ancestorNames: namespaceChainOf(parents, context),
|
|
247576
|
+
element: node
|
|
247577
|
+
},
|
|
247578
|
+
file
|
|
247579
|
+
);
|
|
247580
|
+
warnIfSelfReferential(node, onlyPiece, file);
|
|
247581
|
+
setCompositeName(node, onlyPiece);
|
|
247582
|
+
return;
|
|
247583
|
+
}
|
|
247584
|
+
const compositeName = registerCompositeAssignNames({
|
|
247585
|
+
node,
|
|
247586
|
+
assignNamesValue,
|
|
247587
|
+
fallbackBase: spec.name,
|
|
247588
|
+
ancestorNames: namespaceChainOf(parents, context),
|
|
247589
|
+
context,
|
|
247590
|
+
file,
|
|
247591
|
+
positionMap: makePositionMap(node, spec, file)
|
|
247592
|
+
});
|
|
247593
|
+
if (compositeName === void 0) {
|
|
247594
|
+
deleteAssignNames(node);
|
|
247595
|
+
return;
|
|
247596
|
+
}
|
|
247597
|
+
warnIfSelfReferential(node, compositeName, file);
|
|
247598
|
+
setCompositeName(node, compositeName);
|
|
247599
|
+
});
|
|
247600
|
+
};
|
|
247601
|
+
};
|
|
247602
|
+
function warnIfSelfReferential(node, compositeName, file) {
|
|
247603
|
+
let referencesSelf = false;
|
|
247604
|
+
for (const attr of Object.values(node.attributes)) {
|
|
247605
|
+
for (const child of attr.children) {
|
|
247606
|
+
visitAllMacros(child, (macro) => {
|
|
247607
|
+
if (macro.path[0]?.name === compositeName) {
|
|
247608
|
+
referencesSelf = true;
|
|
247609
|
+
}
|
|
247610
|
+
});
|
|
247611
|
+
}
|
|
247612
|
+
}
|
|
247613
|
+
if (!referencesSelf) {
|
|
247614
|
+
return;
|
|
247615
|
+
}
|
|
247616
|
+
file.message(
|
|
247617
|
+
`<${node.name}> is being named "${compositeName}", but one of its own attributes already refers to $${compositeName}. That reference resolved to nothing in v0.6; in v0.7 it is a circular reference and the document will not load.`,
|
|
247618
|
+
{
|
|
247619
|
+
place: node.position,
|
|
247620
|
+
ruleId: "assign-names/self-reference",
|
|
247621
|
+
source: "v06-to-v07"
|
|
247622
|
+
}
|
|
247623
|
+
);
|
|
247624
|
+
}
|
|
247625
|
+
const applyAssignNameRenames = (context) => {
|
|
247626
|
+
return (tree, file) => {
|
|
247627
|
+
applyRefRenames(tree, context.registry, file, context);
|
|
247628
|
+
};
|
|
247629
|
+
};
|
|
245192
247630
|
async function updateSyntaxFromV06toV07_root(dast, options) {
|
|
245193
|
-
|
|
247631
|
+
const assignNamesContext = createAssignNamesContext(dast);
|
|
247632
|
+
let processor = unified$1().use(correctElementCapitalization).use(correctAttributeCapitalization).use(correctComponentTypesAttributeCapitalization).use(upgradeDeprecatedAttributes).use(ensureDollarBeforeNamesOnSpecificAttributes).use(upgradePathSlashesToDots).use(removeNewNamespaceAttribute).use(upgradeRefElement).use(copySourceToExtendOrCopy, assignNamesContext).use(upgradeCollectElement, assignNamesContext).use(upgradeMapElement, assignNamesContext).use(upgradeAssignNames, assignNamesContext).use(upgradeCopyElements, assignNamesContext).use(applyAssignNameRenames, assignNamesContext).use(upgradeListProps).use(upgradeModuleElement).use(removeConstraintsElement).use(removeDefinitionsElement);
|
|
245194
247633
|
if (!options.doNotUpgradeAttributeSyntax) {
|
|
245195
|
-
processor = processor.use(upgradeAttributeSyntax);
|
|
247634
|
+
processor = processor.use(upgradeAttributeSyntax, assignNamesContext);
|
|
245196
247635
|
}
|
|
245197
247636
|
if (!options.doNotUpgradeCopyTags) {
|
|
245198
247637
|
processor = processor.use(upgradeCopySyntax);
|
|
@@ -245233,31 +247672,44 @@ const ensureDollarBeforeNamesOnSpecificAttributes = () => {
|
|
|
245233
247672
|
});
|
|
245234
247673
|
};
|
|
245235
247674
|
};
|
|
245236
|
-
const copySourceToExtendOrCopy = () => {
|
|
245237
|
-
return (tree) => {
|
|
245238
|
-
visit(tree, (node) => {
|
|
247675
|
+
const copySourceToExtendOrCopy = (context) => {
|
|
247676
|
+
return (tree, file) => {
|
|
247677
|
+
visit(tree, (node, info) => {
|
|
245239
247678
|
if (!isDastElement(node)) {
|
|
245240
247679
|
return;
|
|
245241
247680
|
}
|
|
245242
247681
|
const copySourceAttr = node.attributes["copySource"];
|
|
245243
247682
|
const linkAttr = node.attributes["link"];
|
|
245244
|
-
const assignNamesAttr = node.attributes["assignNames"];
|
|
245245
247683
|
const copyPropAttr = node.attributes["copyProp"];
|
|
245246
247684
|
if (!copySourceAttr) {
|
|
245247
247685
|
return;
|
|
245248
247686
|
}
|
|
245249
|
-
const targetTag =
|
|
247687
|
+
const targetTag = linkAttr ? isV06True(linkAttr) ? "extend" : "copy" : isModuleComponentType(node.name) ? "copy" : "extend";
|
|
245250
247688
|
const baseValue = toXml(copySourceAttr.children).trim();
|
|
245251
247689
|
let extendValue = baseValue.startsWith("$") ? baseValue : `$${baseValue}`;
|
|
245252
247690
|
copySourceAttr.name = targetTag;
|
|
247691
|
+
let appendedPropName;
|
|
245253
247692
|
if (copyPropAttr) {
|
|
245254
|
-
|
|
247693
|
+
appendedPropName = toXml(copyPropAttr.children).trim();
|
|
247694
|
+
extendValue += `.${appendedPropName}`;
|
|
245255
247695
|
delete node.attributes["copyProp"];
|
|
245256
247696
|
}
|
|
245257
247697
|
copySourceAttr.children = reparseAttribute(extendValue);
|
|
245258
|
-
if (
|
|
245259
|
-
|
|
247698
|
+
if (appendedPropName) {
|
|
247699
|
+
const macro = copySourceAttr.children[0];
|
|
247700
|
+
if (macro?.type === "macro") {
|
|
247701
|
+
const last2 = macro.path[macro.path.length - 1];
|
|
247702
|
+
if (last2) {
|
|
247703
|
+
markAsPropAccess(last2);
|
|
247704
|
+
}
|
|
247705
|
+
}
|
|
245260
247706
|
}
|
|
247707
|
+
convertAssignNames(
|
|
247708
|
+
node,
|
|
247709
|
+
namespaceChainOf(info.parents, context),
|
|
247710
|
+
context,
|
|
247711
|
+
file
|
|
247712
|
+
);
|
|
245261
247713
|
if (linkAttr) {
|
|
245262
247714
|
delete node.attributes["link"];
|
|
245263
247715
|
}
|
|
@@ -245292,4 +247744,4 @@ export {
|
|
|
245292
247744
|
getDefaultExportFromCjs$1 as g,
|
|
245293
247745
|
updateSyntaxFromV06toV07 as u
|
|
245294
247746
|
};
|
|
245295
|
-
//# sourceMappingURL=index-
|
|
247747
|
+
//# sourceMappingURL=index-D-2i13ZI.js.map
|