ixbrl-viewer 1.4.1__py3-none-any.whl → 1.4.86__py3-none-any.whl

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.
Files changed (190) hide show
  1. iXBRLViewerPlugin/__init__.py +231 -127
  2. iXBRLViewerPlugin/_version.py +33 -3
  3. iXBRLViewerPlugin/constants.py +96 -2
  4. iXBRLViewerPlugin/featureConfig.py +8 -1
  5. iXBRLViewerPlugin/iXBRLViewer.py +356 -214
  6. iXBRLViewerPlugin/plugin.py +12 -0
  7. iXBRLViewerPlugin/ui.py +81 -50
  8. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
  9. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt +12 -5
  10. iXBRLViewerPlugin/viewer/i18next-parser.config.js +1 -1
  11. iXBRLViewerPlugin/viewer/src/data/utr.json +1244 -0
  12. iXBRLViewerPlugin/viewer/src/html/fact-details.html +69 -38
  13. iXBRLViewerPlugin/viewer/src/html/footer-logo.html +4 -0
  14. iXBRLViewerPlugin/viewer/src/html/footnote-details.html +2 -2
  15. iXBRLViewerPlugin/viewer/src/html/inspector.html +352 -197
  16. iXBRLViewerPlugin/viewer/src/i18n/cy/balancetypes.json +1 -0
  17. iXBRLViewerPlugin/viewer/src/i18n/cy/currencies.json +13 -0
  18. iXBRLViewerPlugin/viewer/src/i18n/cy/datatypes.json +9 -0
  19. iXBRLViewerPlugin/viewer/src/i18n/cy/labelroles.json +24 -0
  20. iXBRLViewerPlugin/viewer/src/i18n/cy/referenceparts.json +10 -0
  21. iXBRLViewerPlugin/viewer/src/i18n/cy/scale.json +16 -0
  22. iXBRLViewerPlugin/viewer/src/i18n/cy/tooltips.json +17 -0
  23. iXBRLViewerPlugin/viewer/src/i18n/cy/translation.json +179 -0
  24. iXBRLViewerPlugin/viewer/src/i18n/da/balancetypes.json +4 -0
  25. iXBRLViewerPlugin/viewer/src/i18n/da/currencies.json +13 -0
  26. iXBRLViewerPlugin/viewer/src/i18n/da/datatypes.json +9 -0
  27. iXBRLViewerPlugin/viewer/src/i18n/da/labelroles.json +24 -0
  28. iXBRLViewerPlugin/viewer/src/i18n/da/referenceparts.json +10 -0
  29. iXBRLViewerPlugin/viewer/src/i18n/da/scale.json +15 -0
  30. iXBRLViewerPlugin/viewer/src/i18n/da/tooltips.json +17 -0
  31. iXBRLViewerPlugin/viewer/src/i18n/da/translation.json +179 -0
  32. iXBRLViewerPlugin/viewer/src/i18n/de/balancetypes.json +4 -0
  33. iXBRLViewerPlugin/viewer/src/i18n/de/currencies.json +13 -0
  34. iXBRLViewerPlugin/viewer/src/i18n/de/datatypes.json +9 -0
  35. iXBRLViewerPlugin/viewer/src/i18n/de/labelroles.json +24 -0
  36. iXBRLViewerPlugin/viewer/src/i18n/de/referenceparts.json +10 -0
  37. iXBRLViewerPlugin/viewer/src/i18n/de/scale.json +15 -0
  38. iXBRLViewerPlugin/viewer/src/i18n/de/tooltips.json +17 -0
  39. iXBRLViewerPlugin/viewer/src/i18n/de/translation.json +179 -0
  40. iXBRLViewerPlugin/viewer/src/i18n/en/balancetypes.json +4 -0
  41. iXBRLViewerPlugin/viewer/src/i18n/en/datatypes.json +10 -0
  42. iXBRLViewerPlugin/viewer/src/i18n/en/labelroles.json +4 -0
  43. iXBRLViewerPlugin/viewer/src/i18n/en/scale.json +16 -0
  44. iXBRLViewerPlugin/viewer/src/i18n/en/tooltips.json +17 -0
  45. iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +73 -23
  46. iXBRLViewerPlugin/viewer/src/i18n/es/balancetypes.json +4 -0
  47. iXBRLViewerPlugin/viewer/src/i18n/es/datatypes.json +10 -0
  48. iXBRLViewerPlugin/viewer/src/i18n/es/labelroles.json +24 -0
  49. iXBRLViewerPlugin/viewer/src/i18n/es/scale.json +16 -0
  50. iXBRLViewerPlugin/viewer/src/i18n/es/tooltips.json +17 -0
  51. iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +87 -37
  52. iXBRLViewerPlugin/viewer/src/i18n/fr/balancetypes.json +4 -0
  53. iXBRLViewerPlugin/viewer/src/i18n/fr/currencies.json +13 -0
  54. iXBRLViewerPlugin/viewer/src/i18n/fr/datatypes.json +9 -0
  55. iXBRLViewerPlugin/viewer/src/i18n/fr/labelroles.json +24 -0
  56. iXBRLViewerPlugin/viewer/src/i18n/fr/referenceparts.json +10 -0
  57. iXBRLViewerPlugin/viewer/src/i18n/fr/scale.json +15 -0
  58. iXBRLViewerPlugin/viewer/src/i18n/fr/tooltips.json +17 -0
  59. iXBRLViewerPlugin/viewer/src/i18n/fr/translation.json +179 -0
  60. iXBRLViewerPlugin/viewer/src/i18n/nl/balancetypes.json +4 -0
  61. iXBRLViewerPlugin/viewer/src/i18n/nl/currencies.json +13 -0
  62. iXBRLViewerPlugin/viewer/src/i18n/nl/datatypes.json +9 -0
  63. iXBRLViewerPlugin/viewer/src/i18n/nl/labelroles.json +24 -0
  64. iXBRLViewerPlugin/viewer/src/i18n/nl/referenceparts.json +10 -0
  65. iXBRLViewerPlugin/viewer/src/i18n/nl/scale.json +15 -0
  66. iXBRLViewerPlugin/viewer/src/i18n/nl/tooltips.json +17 -0
  67. iXBRLViewerPlugin/viewer/src/i18n/nl/translation.json +179 -0
  68. iXBRLViewerPlugin/viewer/src/i18n/uk/balancetypes.json +4 -0
  69. iXBRLViewerPlugin/viewer/src/i18n/uk/currencies.json +13 -0
  70. iXBRLViewerPlugin/viewer/src/i18n/uk/datatypes.json +9 -0
  71. iXBRLViewerPlugin/viewer/src/i18n/uk/labelroles.json +24 -0
  72. iXBRLViewerPlugin/viewer/src/i18n/uk/referenceparts.json +10 -0
  73. iXBRLViewerPlugin/viewer/src/i18n/uk/scale.json +15 -0
  74. iXBRLViewerPlugin/viewer/src/i18n/uk/tooltips.json +17 -0
  75. iXBRLViewerPlugin/viewer/src/i18n/uk/translation.json +179 -0
  76. iXBRLViewerPlugin/viewer/src/icons/calculator.svg +13 -0
  77. iXBRLViewerPlugin/viewer/src/icons/circle-cross.svg +11 -0
  78. iXBRLViewerPlugin/viewer/src/icons/circle-tick.svg +11 -0
  79. iXBRLViewerPlugin/viewer/src/icons/dark-mode.svg +4 -0
  80. iXBRLViewerPlugin/viewer/src/icons/dimension.svg +1 -5
  81. iXBRLViewerPlugin/viewer/src/icons/member.svg +2 -5
  82. iXBRLViewerPlugin/viewer/src/icons/multi-tag.svg +10 -0
  83. iXBRLViewerPlugin/viewer/src/img/arelle-dark.svg +179 -0
  84. iXBRLViewerPlugin/viewer/src/img/inline-viewer-dark.svg +59 -0
  85. iXBRLViewerPlugin/viewer/src/js/accordian.js +5 -4
  86. iXBRLViewerPlugin/viewer/src/js/aspect.js +29 -10
  87. iXBRLViewerPlugin/viewer/src/js/aspect.test.js +40 -31
  88. iXBRLViewerPlugin/viewer/src/js/balance.js +14 -0
  89. iXBRLViewerPlugin/viewer/src/js/calculation.js +213 -0
  90. iXBRLViewerPlugin/viewer/src/js/calculation.test.js +306 -0
  91. iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +187 -0
  92. iXBRLViewerPlugin/viewer/src/js/chart.js +26 -24
  93. iXBRLViewerPlugin/viewer/src/js/chart.test.js +10 -9
  94. iXBRLViewerPlugin/viewer/src/js/concept.js +37 -4
  95. iXBRLViewerPlugin/viewer/src/js/concept.test.js +30 -6
  96. iXBRLViewerPlugin/viewer/src/js/datatype.js +20 -0
  97. iXBRLViewerPlugin/viewer/src/js/datatype.test.js +62 -0
  98. iXBRLViewerPlugin/viewer/src/js/dialog.js +6 -4
  99. iXBRLViewerPlugin/viewer/src/js/docOrderIndex.js +7 -7
  100. iXBRLViewerPlugin/viewer/src/js/fact.js +156 -59
  101. iXBRLViewerPlugin/viewer/src/js/fact.test.js +160 -29
  102. iXBRLViewerPlugin/viewer/src/js/factset.js +64 -15
  103. iXBRLViewerPlugin/viewer/src/js/factset.test.js +102 -31
  104. iXBRLViewerPlugin/viewer/src/js/footnote.js +8 -2
  105. iXBRLViewerPlugin/viewer/src/js/index.js +11 -3
  106. iXBRLViewerPlugin/viewer/src/js/inspector.js +747 -221
  107. iXBRLViewerPlugin/viewer/src/js/inspector.test.js +143 -25
  108. iXBRLViewerPlugin/viewer/src/js/interval.js +70 -0
  109. iXBRLViewerPlugin/viewer/src/js/interval.test.js +153 -0
  110. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.js +391 -262
  111. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.test.js +134 -20
  112. iXBRLViewerPlugin/viewer/src/js/ixnode.js +1 -1
  113. iXBRLViewerPlugin/viewer/src/js/menu.js +25 -7
  114. iXBRLViewerPlugin/viewer/src/js/number-matcher.js +7 -3
  115. iXBRLViewerPlugin/viewer/src/js/number-matcher.test.js +4 -0
  116. iXBRLViewerPlugin/viewer/src/js/outline.js +34 -13
  117. iXBRLViewerPlugin/viewer/src/js/outline.test.js +97 -91
  118. iXBRLViewerPlugin/viewer/src/js/period.js +0 -1
  119. iXBRLViewerPlugin/viewer/src/js/report.js +260 -351
  120. iXBRLViewerPlugin/viewer/src/js/report.test.js +95 -27
  121. iXBRLViewerPlugin/viewer/src/js/reportset.js +264 -0
  122. iXBRLViewerPlugin/viewer/src/js/reportset.test.js +357 -0
  123. iXBRLViewerPlugin/viewer/src/js/search.js +72 -38
  124. iXBRLViewerPlugin/viewer/src/js/search.test.js +184 -84
  125. iXBRLViewerPlugin/viewer/src/js/summary.js +34 -8
  126. iXBRLViewerPlugin/viewer/src/js/summary.test.js +69 -25
  127. iXBRLViewerPlugin/viewer/src/js/tableExport.js +9 -9
  128. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.js +34 -0
  129. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.test.js +32 -0
  130. iXBRLViewerPlugin/viewer/src/js/test-utils.js +46 -0
  131. iXBRLViewerPlugin/viewer/src/js/theme.js +50 -0
  132. iXBRLViewerPlugin/viewer/src/js/unit.js +90 -32
  133. iXBRLViewerPlugin/viewer/src/js/unit.test.js +62 -25
  134. iXBRLViewerPlugin/viewer/src/js/util.js +94 -0
  135. iXBRLViewerPlugin/viewer/src/js/util.test.js +33 -1
  136. iXBRLViewerPlugin/viewer/src/js/utr.js +27 -0
  137. iXBRLViewerPlugin/viewer/src/js/viewer.js +205 -181
  138. iXBRLViewerPlugin/viewer/src/js/viewerOptions.js +0 -2
  139. iXBRLViewerPlugin/viewer/src/less/accordian.less +10 -6
  140. iXBRLViewerPlugin/viewer/src/less/block-list.less +16 -5
  141. iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +83 -0
  142. iXBRLViewerPlugin/viewer/src/less/chart.less +8 -5
  143. iXBRLViewerPlugin/viewer/src/less/colours-dark-mode.less +40 -0
  144. iXBRLViewerPlugin/viewer/src/less/colours.less +32 -20
  145. iXBRLViewerPlugin/viewer/src/less/common.less +3 -3
  146. iXBRLViewerPlugin/viewer/src/less/components.less +6 -4
  147. iXBRLViewerPlugin/viewer/src/less/core.less +2 -0
  148. iXBRLViewerPlugin/viewer/src/less/dialog.less +21 -14
  149. iXBRLViewerPlugin/viewer/src/less/form-controls.less +33 -11
  150. iXBRLViewerPlugin/viewer/src/less/inspector.less +1045 -726
  151. iXBRLViewerPlugin/viewer/src/less/loader.less +2 -2
  152. iXBRLViewerPlugin/viewer/src/less/menu.less +33 -15
  153. iXBRLViewerPlugin/viewer/src/less/summary.less +16 -6
  154. iXBRLViewerPlugin/viewer/src/less/tabs.less +9 -9
  155. iXBRLViewerPlugin/viewer/src/less/text-block-viewer.less +2 -0
  156. iXBRLViewerPlugin/viewer/src/less/text-mixins.less +2 -1
  157. iXBRLViewerPlugin/viewer/src/less/validation-report.less +2 -3
  158. iXBRLViewerPlugin/viewer/src/less/viewer.less +105 -74
  159. iXBRLViewerPlugin/viewer/webpack.common.js +19 -9
  160. iXBRLViewerPlugin/xhtmlserialize.py +59 -45
  161. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/METADATA +181 -50
  162. ixbrl_viewer-1.4.86.dist-info/RECORD +217 -0
  163. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/WHEEL +1 -1
  164. ixbrl_viewer-1.4.1.dist-info/LICENSE → ixbrl_viewer-1.4.86.dist-info/licenses/LICENSE.md +8 -14
  165. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/top_level.txt +0 -1
  166. iXBRLViewerPlugin/viewer/src/js/calculations.js +0 -111
  167. iXBRLViewerPlugin/viewer/src/js/interact.min.js +0 -6
  168. ixbrl_viewer-1.4.1.dist-info/RECORD +0 -155
  169. tests/__init__.py +0 -0
  170. tests/puppeteer/framework/core_elements.js +0 -117
  171. tests/puppeteer/framework/page_objects/doc_frame.js +0 -105
  172. tests/puppeteer/framework/page_objects/fact_details_panel.js +0 -80
  173. tests/puppeteer/framework/page_objects/search_panel.js +0 -76
  174. tests/puppeteer/framework/page_objects/toolbar.js +0 -18
  175. tests/puppeteer/framework/utils.js +0 -3
  176. tests/puppeteer/framework/viewer_page.js +0 -103
  177. tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
  178. tests/puppeteer/test_filings/filing_documents_smoke_test.zip +0 -0
  179. tests/puppeteer/test_filings/highlights.zip +0 -0
  180. tests/puppeteer/tests/fact_properties.test.js +0 -78
  181. tests/puppeteer/tests/highlight.test.js +0 -186
  182. tests/puppeteer/tests/search.test.js +0 -86
  183. tests/puppeteer/tools/generate.sh +0 -15
  184. tests/unit_tests/__init__.py +0 -0
  185. tests/unit_tests/iXBRLViewerPlugin/__init__.py +0 -0
  186. tests/unit_tests/iXBRLViewerPlugin/mock_arelle.py +0 -39
  187. tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +0 -641
  188. tests/unit_tests/iXBRLViewerPlugin/test_xhtmlserialize.py +0 -310
  189. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/entry_points.txt +0 -0
  190. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info/licenses}/NOTICE +0 -0
@@ -4,44 +4,62 @@ import $ from 'jquery'
4
4
  import i18next from "i18next";
5
5
  import { Aspect } from "./aspect.js";
6
6
  import { Period } from './period.js';
7
- import { formatNumber } from "./util.js";
7
+ import { formatNumber, localId } from "./util.js";
8
8
  import Decimal from "decimal.js";
9
+ import { Interval } from './interval.js';
9
10
 
10
11
  export class Fact {
11
12
 
12
- constructor(report, factId) {
13
- this.f = report.data.facts[factId];
14
- this.ixNode = report.getIXNodeForItemId(factId);
15
- this._report = report;
16
- this.id = factId;
13
+ constructor(report, factId, factData) {
14
+ this.f = factData;
15
+ this.ixNode = report.reportSet.getIXNodeForItemId(factId);
16
+ this.report = report;
17
+ this.vuid = factId;
17
18
  this.linkedFacts = [];
19
+ this._footnotes = [];
18
20
  }
19
21
 
20
- report() {
21
- return this._report;
22
+ localId() {
23
+ return localId(this.vuid);
24
+ }
25
+
26
+ isMandatory() {
27
+ return this.f.a.m
22
28
  }
23
29
 
24
30
  getLabel(rolePrefix, withPrefix) {
25
- return this._report.getLabel(this.f.a.c, rolePrefix, withPrefix);
31
+ return this.report.getLabel(this.f.a.c, rolePrefix, withPrefix);
32
+ }
33
+
34
+ getLabelAndLang(rolePrefix, withPrefix) {
35
+ return this.report.getLabelAndLang(this.f.a.c, rolePrefix, withPrefix);
26
36
  }
27
37
 
28
38
  getLabelOrName(rolePrefix, withPrefix) {
29
- return this._report.getLabelOrName(this.f.a.c, rolePrefix, withPrefix);
39
+ return this.report.getLabelOrName(this.f.a.c, rolePrefix, withPrefix);
40
+ }
41
+
42
+ getLabelOrNameAndLang(rolePrefix, withPrefix) {
43
+ return this.report.getLabelOrNameAndLang(this.f.a.c, rolePrefix, withPrefix);
30
44
  }
31
45
 
32
46
  conceptName() {
33
47
  return this.f.a.c;
34
48
  }
35
49
 
50
+ conceptDisplayName() {
51
+ return this.report.reportSet.taxonomyNamer.convertQName(this.conceptQName());
52
+ }
53
+
36
54
  concept() {
37
- return this._report.getConcept(this.f.a.c);
55
+ return this.report.getConcept(this.f.a.c);
38
56
  }
39
57
 
40
58
  conceptQName() {
41
- return this._report.qname(this.f.a.c);
59
+ return this.report.qname(this.f.a.c);
42
60
  }
43
61
 
44
- period(){
62
+ period() {
45
63
  return new Period(this.f.a.p);
46
64
  }
47
65
 
@@ -62,10 +80,17 @@ export class Fact {
62
80
  return this.f.v;
63
81
  }
64
82
 
65
- readableValue() {
66
- let v = this.f.v;
83
+ readableValue(val) {
84
+ return this.readableValueHTML(val).textContent;
85
+ }
86
+
87
+ readableValueHTML(val) {
88
+ let v = val === undefined ? this.f.v : val;
89
+ const span = document.createElement("span");
90
+ span.classList.add("fact-value");
67
91
  if (this.isInvalidIXValue()) {
68
- v = "Invalid value";
92
+ span.classList.add("fact-value-invalid");
93
+ span.append(document.createTextNode("Invalid value"));
69
94
  }
70
95
  else if (this.isNumeric()) {
71
96
  const d = this.decimals();
@@ -77,16 +102,19 @@ export class Fact {
77
102
  formattedNumber = formatNumber(v, d);
78
103
  }
79
104
  if (this.isMonetaryValue()) {
80
- v = this.measureLabel() + " " + formattedNumber;
105
+ span.append(this.unitLabelHTML());
106
+ span.append(document.createTextNode(" " + formattedNumber));
81
107
  }
82
108
  else {
83
- v = formattedNumber + " " + this.measureLabel();
109
+ span.append(document.createTextNode(formattedNumber + " "));
110
+ span.append(this.unitLabelHTML());
84
111
  }
85
112
  }
86
113
  else if (this.isNil()) {
87
- v = "nil";
114
+ span.classList.add("fact-value-nil");
115
+ span.append(document.createTextNode("nil"));
88
116
  }
89
- else if (this.escaped()) {
117
+ else if (this.isTextBlock()) {
90
118
  const html = $("<div>").append($($.parseHTML(v, null, false)));
91
119
  /* Insert an extra space at the beginning and end of block elements to
92
120
  * preserve separation of sections of text. */
@@ -96,31 +124,20 @@ export class Fact {
96
124
  .prepend(document.createTextNode(' '));
97
125
  /* Replace runs of whitespace (including nbsp) with a single space */
98
126
  v = html.text().replace(/[\u00a0\s]+/g, " ").trim();
127
+ span.append(document.createTextNode(v));
99
128
  }
100
129
  else if (this.isEnumeration()) {
101
130
  const labels = [];
102
131
  for (const qn of v.split(' ')) {
103
- labels.push(this._report.getLabelOrName(qn, 'std'));
132
+ labels.push(this.report.getLabelOrName(qn, 'std'));
104
133
  }
105
134
  v = labels.join(', ');
135
+ span.append(document.createTextNode(v));
106
136
  }
107
- return v;
108
- }
109
-
110
- /**
111
- * Returns the qname of the first numerator in the fact's unit
112
- * @return {String} QName string of a measure
113
- */
114
- measure() {
115
- return this.unit()?.measure();
116
- }
117
-
118
- /**
119
- * Returns a readable label representing the first numerator in the fact's unit
120
- * @return {String} Label representing measure
121
- */
122
- measureLabel() {
123
- return this.unit()?.measureLabel() ?? i18next.t("factDetails.noUnit");
137
+ else {
138
+ span.append(document.createTextNode(v));
139
+ }
140
+ return span;
124
141
  }
125
142
 
126
143
  /**
@@ -133,25 +150,37 @@ export class Fact {
133
150
  if (!unitKey) {
134
151
  return undefined;
135
152
  }
136
- this._unit = this.report().getUnit(unitKey);
153
+ this._unit = this.report.reportSet.getUnit(unitKey);
137
154
  }
138
155
  return this._unit;
139
156
  }
140
157
 
158
+ /**
159
+ * Returns a readable label representing the fact's unit
160
+ * @return {String} Label representing unit
161
+ */
162
+ unitLabel() {
163
+ return this.unit()?.label() ?? i18next.t("factDetails.noUnit");
164
+ }
165
+
166
+ unitLabelHTML() {
167
+ return this.unit()?.html() ?? document.createTextNode(i18next.t("factDetails.noUnit"));
168
+ }
169
+
141
170
  getConceptPrefix() {
142
171
  return this.conceptName().split(':')[0];
143
172
  }
144
173
 
145
174
  isCalculationContributor() {
146
175
  if (this._isCalculationContributor === undefined) {
147
- this._isCalculationContributor = this._report.isCalculationContributor(this.f.a.c);
176
+ this._isCalculationContributor = this.report.isCalculationContributor(this.f.a.c);
148
177
  }
149
178
  return this._isCalculationContributor;
150
179
  }
151
180
 
152
181
  isCalculationSummation() {
153
182
  if (this._isCalculationSummation === undefined) {
154
- this._isCalculationSummation = this._report.isCalculationSummation(this.f.a.c);
183
+ this._isCalculationSummation = this.report.isCalculationSummation(this.f.a.c);
155
184
  }
156
185
  return this._isCalculationSummation;
157
186
  }
@@ -171,11 +200,11 @@ export class Fact {
171
200
  }
172
201
 
173
202
  hasExplicitDimension() {
174
- return Object.keys(this.dimensions()).some(d => !this._report.getConcept(d).isTypedDimension());
203
+ return Object.keys(this.dimensions()).some(d => !this.report.getConcept(d).isTypedDimension());
175
204
  }
176
205
 
177
206
  hasTypedDimension() {
178
- return Object.keys(this.dimensions()).some(d => this._report.getConcept(d).isTypedDimension());
207
+ return Object.keys(this.dimensions()).some(d => this.report.getConcept(d).isTypedDimension());
179
208
  }
180
209
 
181
210
  isMonetaryValue() {
@@ -186,17 +215,30 @@ export class Fact {
186
215
  return this.concept().isTextBlock();
187
216
  }
188
217
 
218
+ /**
219
+ * Get all defined Aspects (sorted by aspect name).
220
+ *
221
+ * @returns {Aspect[]} An array of instantiated Aspect objects.
222
+ */
189
223
  aspects() {
190
- return Object.keys(this.f.a).map(k => this.aspect(k));
224
+ return Object.keys(this.f.a)
225
+ .map(k => this.aspect(k))
226
+ .sort((a, b) => a.compareTo(b));
191
227
  }
192
228
 
229
+ /**
230
+ * Retrieves or lazily creates an Aspect instance for the given aspect key.
231
+ *
232
+ * @param {string} a - The aspect key to retrieve or create.
233
+ * @returns {Aspect|undefined} The Aspect instance associated with the key, or undefined if not defined in `this.f.a`.
234
+ */
193
235
  aspect(a) {
194
236
  if (this._aspects === undefined) {
195
237
  this._aspects = {}
196
238
  }
197
239
  if (this._aspects[a] === undefined) {
198
240
  if (this.f.a[a] !== undefined) {
199
- this._aspects[a] = new Aspect(a, this.f.a[a], this._report);
241
+ this._aspects[a] = new Aspect(a, this.f.a[a], this.report);
200
242
  }
201
243
  }
202
244
  return this._aspects[a];
@@ -242,15 +284,17 @@ export class Fact {
242
284
  }
243
285
 
244
286
  duplicates() {
245
- return this._report.getAlignedFacts(this);
287
+ return this.report.getAlignedFacts(this);
246
288
  }
247
289
 
248
290
  isNil() {
249
291
  return this.f.v === null;
250
292
  }
293
+
251
294
  isNegative() {
252
295
  return this.isNumeric() && !this.isNil() && this.value() !== undefined && new Decimal(this.value()).isNegative() && !this.isZero();
253
296
  }
297
+
254
298
  isPositive() {
255
299
  return this.isNumeric() && !this.isNil() && this.value() !== undefined && new Decimal(this.value()).isPositive() && !this.isZero();
256
300
  }
@@ -258,22 +302,18 @@ export class Fact {
258
302
  isZero() {
259
303
  return this.isNumeric() && !this.isNil() && this.value() !== undefined && new Decimal(this.value()).isZero();
260
304
  }
305
+
261
306
  isInvalidIXValue() {
262
307
  return this.f.err == 'INVALID_IX_VALUE';
263
308
  }
264
309
 
265
310
  getScaleLabel(value, isAccuracy=false) {
266
- let measure = this.measure() ?? '';
267
- if (measure) {
268
- measure = this.report().qname(measure).localname;
269
- }
270
- return this._report.getScaleLabel(
311
+ return this.report.getScaleLabel(
271
312
  // We use the same table of labels for scale and accuracy,
272
313
  // but decimals means "accurate to 10^-N" whereas scale means 10^N,
273
314
  // so invert N for accuracy.
274
315
  isAccuracy ? -value : value,
275
- this.isMonetaryValue(),
276
- measure
316
+ this.unit()
277
317
  );
278
318
  }
279
319
 
@@ -311,7 +351,7 @@ export class Fact {
311
351
  }
312
352
 
313
353
  identifier() {
314
- return this._report.qname(this.f.a.e);
354
+ return this.report.qname(this.f.a.e);
315
355
  }
316
356
 
317
357
  escaped() {
@@ -322,8 +362,12 @@ export class Fact {
322
362
  return this.concept().isEnumeration();
323
363
  }
324
364
 
365
+ addFootnote(fn) {
366
+ this._footnotes.push(fn);
367
+ }
368
+
325
369
  footnotes() {
326
- return (this.f.fn || []).map((fn, i) => this._report.getItemById(fn));
370
+ return this._footnotes;
327
371
  }
328
372
 
329
373
  isHidden() {
@@ -336,7 +380,7 @@ export class Fact {
336
380
 
337
381
  widerConcepts() {
338
382
  const concepts = [];
339
- const parentsByELR = this._report.getParentRelationships(this.conceptName(), "w-n");
383
+ const parentsByELR = this.report.getParentRelationships(this.conceptName(), "w-n");
340
384
  for (const elr in parentsByELR) {
341
385
  concepts.push(...$.map(parentsByELR[elr], (rel) => rel.src));
342
386
  }
@@ -345,16 +389,69 @@ export class Fact {
345
389
 
346
390
  narrowerConcepts() {
347
391
  const concepts = [];
348
- const childrenByELR = this._report.getChildRelationships(this.conceptName(), "w-n");
392
+ const childrenByELR = this.report.getChildRelationships(this.conceptName(), "w-n");
349
393
  for (const elr in childrenByELR) {
350
394
  concepts.push(...$.map(childrenByELR[elr], (rel) => rel.t));
351
395
  }
352
396
  return concepts;
353
397
  }
354
398
 
355
- // Facts that are the source of relationships to this fact.
399
+ /*
400
+ * Facts that are the source of relationships to this fact.
401
+ */
356
402
  addLinkedFact(f) {
357
403
  this.linkedFacts.push(f);
358
404
  }
359
- }
360
405
 
406
+ /*
407
+ * Returns the fact's value, rounded according to the value of its decimals
408
+ * property. This is an odd thing to do, as it implies that more figures
409
+ * were reported than the decimals property suggest are accurate, but this
410
+ * is required for Calc 1.0 validation.
411
+ *
412
+ * valueInterval() is a more meaningful.
413
+ */
414
+ roundedValue() {
415
+ Decimal.rounding = Decimal.ROUND_HALF_UP;
416
+ const v = new Decimal(this.value());
417
+ const d = this.decimals();
418
+ if (d === undefined) {
419
+ return v;
420
+ }
421
+ return v.mul(10 ** d).round().mul(10 ** (0-d));
422
+ }
423
+
424
+ isCompleteDuplicate(other) {
425
+ return this.value() === other.value() && this.decimals() === other.decimals();
426
+ }
427
+
428
+ /*
429
+ * Facts that are the source of relationships to this fact.
430
+ */
431
+ addLinkedFact(f) {
432
+ this.linkedFacts.push(f);
433
+ }
434
+
435
+ /*
436
+ * Returns an Interval for the fact's value, as implied by its decimals
437
+ * property.
438
+ */
439
+ valueInterval() {
440
+ return Interval.fromFact(this);
441
+ }
442
+
443
+ isMorePrecise(of) {
444
+ // decimals of "undefined" indicates infinite precision
445
+ if (of.decimals() === undefined) {
446
+ return false;
447
+ }
448
+ if (this.decimals() === undefined) {
449
+ return true;
450
+ }
451
+ return this.decimals() > of.decimals();
452
+ }
453
+
454
+ targetDocument() {
455
+ return this.report.targetDocument();
456
+ }
457
+ }