ixbrl-viewer 1.4.21__py3-none-any.whl → 1.4.50__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.

Potentially problematic release.


This version of ixbrl-viewer might be problematic. Click here for more details.

Files changed (114) hide show
  1. iXBRLViewerPlugin/__init__.py +77 -49
  2. iXBRLViewerPlugin/_version.py +2 -2
  3. iXBRLViewerPlugin/constants.py +86 -1
  4. iXBRLViewerPlugin/featureConfig.py +4 -1
  5. iXBRLViewerPlugin/iXBRLViewer.py +210 -137
  6. iXBRLViewerPlugin/plugin.py +7 -0
  7. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
  8. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt +9 -2
  9. iXBRLViewerPlugin/viewer/i18next-parser.config.js +1 -1
  10. iXBRLViewerPlugin/viewer/src/data/utr.json +1 -0
  11. iXBRLViewerPlugin/viewer/src/html/fact-details.html +69 -38
  12. iXBRLViewerPlugin/viewer/src/html/footer-logo.html +4 -0
  13. iXBRLViewerPlugin/viewer/src/html/footnote-details.html +1 -1
  14. iXBRLViewerPlugin/viewer/src/html/inspector.html +324 -211
  15. iXBRLViewerPlugin/viewer/src/i18n/cy/balancetypes.json +1 -0
  16. iXBRLViewerPlugin/viewer/src/i18n/cy/currencies.json +13 -0
  17. iXBRLViewerPlugin/viewer/src/i18n/cy/datatypes.json +9 -0
  18. iXBRLViewerPlugin/viewer/src/i18n/cy/labelroles.json +24 -0
  19. iXBRLViewerPlugin/viewer/src/i18n/cy/referenceparts.json +10 -0
  20. iXBRLViewerPlugin/viewer/src/i18n/cy/scale.json +16 -0
  21. iXBRLViewerPlugin/viewer/src/i18n/cy/tooltips.json +17 -0
  22. iXBRLViewerPlugin/viewer/src/i18n/cy/translation.json +179 -0
  23. iXBRLViewerPlugin/viewer/src/i18n/en/balancetypes.json +4 -0
  24. iXBRLViewerPlugin/viewer/src/i18n/en/datatypes.json +10 -0
  25. iXBRLViewerPlugin/viewer/src/i18n/en/labelroles.json +4 -0
  26. iXBRLViewerPlugin/viewer/src/i18n/en/scale.json +16 -0
  27. iXBRLViewerPlugin/viewer/src/i18n/en/tooltips.json +17 -0
  28. iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +56 -24
  29. iXBRLViewerPlugin/viewer/src/i18n/es/balancetypes.json +4 -0
  30. iXBRLViewerPlugin/viewer/src/i18n/es/datatypes.json +10 -0
  31. iXBRLViewerPlugin/viewer/src/i18n/es/labelroles.json +24 -0
  32. iXBRLViewerPlugin/viewer/src/i18n/es/scale.json +16 -0
  33. iXBRLViewerPlugin/viewer/src/i18n/es/tooltips.json +17 -0
  34. iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +70 -37
  35. iXBRLViewerPlugin/viewer/src/icons/dark-mode.svg +4 -0
  36. iXBRLViewerPlugin/viewer/src/img/arelle-dark.svg +179 -0
  37. iXBRLViewerPlugin/viewer/src/img/inline-viewer-dark.svg +59 -0
  38. iXBRLViewerPlugin/viewer/src/js/accordian.js +3 -2
  39. iXBRLViewerPlugin/viewer/src/js/aspect.js +18 -10
  40. iXBRLViewerPlugin/viewer/src/js/aspect.test.js +2 -2
  41. iXBRLViewerPlugin/viewer/src/js/balance.js +14 -0
  42. iXBRLViewerPlugin/viewer/src/js/calculation.js +45 -34
  43. iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +4 -7
  44. iXBRLViewerPlugin/viewer/src/js/chart.js +23 -21
  45. iXBRLViewerPlugin/viewer/src/js/concept.js +30 -3
  46. iXBRLViewerPlugin/viewer/src/js/concept.test.js +23 -2
  47. iXBRLViewerPlugin/viewer/src/js/datatype.js +20 -0
  48. iXBRLViewerPlugin/viewer/src/js/datatype.test.js +62 -0
  49. iXBRLViewerPlugin/viewer/src/js/dialog.js +6 -4
  50. iXBRLViewerPlugin/viewer/src/js/fact.js +40 -7
  51. iXBRLViewerPlugin/viewer/src/js/fact.test.js +3 -0
  52. iXBRLViewerPlugin/viewer/src/js/index.js +11 -3
  53. iXBRLViewerPlugin/viewer/src/js/inspector.js +560 -160
  54. iXBRLViewerPlugin/viewer/src/js/inspector.test.js +1 -2
  55. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.js +129 -31
  56. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.test.js +133 -20
  57. iXBRLViewerPlugin/viewer/src/js/ixnode.js +1 -1
  58. iXBRLViewerPlugin/viewer/src/js/menu.js +25 -7
  59. iXBRLViewerPlugin/viewer/src/js/number-matcher.js +2 -2
  60. iXBRLViewerPlugin/viewer/src/js/outline.js +2 -4
  61. iXBRLViewerPlugin/viewer/src/js/period.js +0 -1
  62. iXBRLViewerPlugin/viewer/src/js/report.js +68 -13
  63. iXBRLViewerPlugin/viewer/src/js/report.test.js +77 -6
  64. iXBRLViewerPlugin/viewer/src/js/reportset.js +33 -3
  65. iXBRLViewerPlugin/viewer/src/js/reportset.test.js +32 -6
  66. iXBRLViewerPlugin/viewer/src/js/search.js +61 -35
  67. iXBRLViewerPlugin/viewer/src/js/search.test.js +8 -5
  68. iXBRLViewerPlugin/viewer/src/js/summary.js +28 -2
  69. iXBRLViewerPlugin/viewer/src/js/summary.test.js +52 -14
  70. iXBRLViewerPlugin/viewer/src/js/tableExport.js +3 -3
  71. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.js +34 -0
  72. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.test.js +32 -0
  73. iXBRLViewerPlugin/viewer/src/js/theme.js +49 -0
  74. iXBRLViewerPlugin/viewer/src/js/unit.js +73 -2
  75. iXBRLViewerPlugin/viewer/src/js/unit.test.js +14 -3
  76. iXBRLViewerPlugin/viewer/src/js/util.js +21 -18
  77. iXBRLViewerPlugin/viewer/src/js/util.test.js +1 -0
  78. iXBRLViewerPlugin/viewer/src/js/utr.js +27 -0
  79. iXBRLViewerPlugin/viewer/src/js/viewer.js +40 -29
  80. iXBRLViewerPlugin/viewer/src/js/viewerOptions.js +0 -2
  81. iXBRLViewerPlugin/viewer/src/less/accordian.less +8 -4
  82. iXBRLViewerPlugin/viewer/src/less/block-list.less +12 -6
  83. iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +2 -2
  84. iXBRLViewerPlugin/viewer/src/less/chart.less +8 -5
  85. iXBRLViewerPlugin/viewer/src/less/colours-dark-mode.less +40 -0
  86. iXBRLViewerPlugin/viewer/src/less/colours.less +28 -21
  87. iXBRLViewerPlugin/viewer/src/less/common.less +1 -1
  88. iXBRLViewerPlugin/viewer/src/less/components.less +3 -3
  89. iXBRLViewerPlugin/viewer/src/less/core.less +2 -0
  90. iXBRLViewerPlugin/viewer/src/less/dialog.less +13 -10
  91. iXBRLViewerPlugin/viewer/src/less/form-controls.less +33 -11
  92. iXBRLViewerPlugin/viewer/src/less/inspector.less +556 -300
  93. iXBRLViewerPlugin/viewer/src/less/loader.less +2 -2
  94. iXBRLViewerPlugin/viewer/src/less/menu.less +33 -15
  95. iXBRLViewerPlugin/viewer/src/less/summary.less +16 -6
  96. iXBRLViewerPlugin/viewer/src/less/tabs.less +5 -5
  97. iXBRLViewerPlugin/viewer/src/less/text-mixins.less +2 -1
  98. iXBRLViewerPlugin/viewer/src/less/validation-report.less +1 -1
  99. iXBRLViewerPlugin/viewer/src/less/viewer.less +30 -18
  100. iXBRLViewerPlugin/viewer/webpack.common.js +19 -9
  101. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/METADATA +41 -14
  102. ixbrl_viewer-1.4.50.dist-info/RECORD +197 -0
  103. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/WHEEL +1 -1
  104. tests/puppeteer/framework/page_objects/doc_frame.js +3 -3
  105. tests/puppeteer/framework/page_objects/fact_details_panel.js +29 -1
  106. tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
  107. tests/puppeteer/tests/fact_properties.test.js +10 -4
  108. tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +117 -51
  109. iXBRLViewerPlugin/viewer/src/js/interact.min.js +0 -6
  110. ixbrl_viewer-1.4.21.dist-info/RECORD +0 -166
  111. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/LICENSE +0 -0
  112. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/NOTICE +0 -0
  113. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/entry_points.txt +0 -0
  114. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,7 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
- import { Fact } from "./fact.js"
4
- import { Footnote } from "./footnote.js"
5
- import { QName } from "./qname.js"
6
3
  import { Concept } from "./concept.js";
7
- import { setDefault, viewerUniqueId } from "./util.js";
8
- import $ from 'jquery'
4
+ import { setDefault } from "./util.js";
9
5
  import i18next from "i18next";
10
6
 
11
7
  // Class to represent the XBRL data from a single target document in a single
@@ -169,7 +165,39 @@ export class XBRLReport {
169
165
  return label;
170
166
  }
171
167
  }
172
- return this.reportSet.roleMap()[rolePrefix];
168
+ return undefined;
169
+ }
170
+
171
+ getRoleLabelOrURI(rolePrefix) {
172
+ return this.getRoleLabel(rolePrefix) ?? this.reportSet.roleMap()[rolePrefix];
173
+ }
174
+
175
+ getLabelRoleLabel(rolePrefix) {
176
+ const roleURI = this.reportSet.roleMap()[rolePrefix];
177
+ if (roleURI === undefined) {
178
+ return undefined;
179
+ }
180
+
181
+ // Built-in label roles don't have a definition in the taxonomy. Do an
182
+ // i18n look-up on the last part of the URI. For "en" the camel-case
183
+ // splitter will do what we want for everything except standard label
184
+ // and documentation label
185
+ const suffix = roleURI.split("/").pop();
186
+ if (roleURI.startsWith("http://www.xbrl.org/2003/role/") && i18next.exists(`labelRoles:${suffix}`)) {
187
+ return i18next.t(`labelRoles:${suffix}`);
188
+ }
189
+
190
+ // Attempt to get a label from the role definition
191
+ const label = this.getRoleLabel(rolePrefix);
192
+ if (label !== undefined) {
193
+ return label;
194
+ }
195
+
196
+ // Fall back on de-camel-casing the last part of the URI
197
+ return suffix
198
+ .replaceAll(/([A-Z][a-z]+)/g, ' $1')
199
+ .trim()
200
+ .replace(/^./, s => s.toUpperCase());
173
201
  }
174
202
 
175
203
  localDocuments() {
@@ -184,7 +212,7 @@ export class XBRLReport {
184
212
  }
185
213
 
186
214
  getScaleLabel(scale, unit) {
187
- let label = i18next.t(`scale.${scale}`, {defaultValue:"noName"});
215
+ let label = i18next.t(`scale:${scale}`, {defaultValue:"noName"});
188
216
  if (unit && unit.isMonetary() && scale === -2) {
189
217
  let measure = unit.value() ?? '';
190
218
  if (measure) {
@@ -203,35 +231,47 @@ export class XBRLReport {
203
231
  }
204
232
 
205
233
  getLabel(c, rolePrefix, showPrefix) {
234
+ return this.getLabelAndLang(c, rolePrefix, showPrefix).label;
235
+ }
236
+
237
+ getLabelAndLang(c, rolePrefix, showPrefix) {
206
238
  rolePrefix = rolePrefix || 'std';
207
239
  const lang = this.reportSet.viewerOptions.language;
208
240
  const concept = this._reportData.concepts[c];
209
241
  if (concept === undefined) {
210
242
  console.log("Attempt to get label for undefined concept: " + c);
211
- return "<no label>";
243
+ return { label: "<no label>" };
212
244
  }
213
245
  const labels = concept.labels[rolePrefix]
214
246
  if (labels === undefined || Object.keys(labels).length == 0) {
215
- return undefined;
247
+ return { label: undefined };
216
248
  }
217
249
  else {
218
250
  let label;
251
+ let actualLang;
219
252
  if (lang && labels[lang]) {
220
253
  label = labels[lang];
254
+ actualLang = lang;
221
255
  }
222
256
  else {
223
257
  // Fall back on English, then any label deterministically.
224
- label = labels["en"] || labels["en-us"] || labels[Object.keys(labels).sort()[0]];
258
+ for (const l of ["en", "en-us", Object.keys(labels).sort()[0]]) {
259
+ if (labels[l] !== undefined) {
260
+ label = labels[l];
261
+ actualLang = l;
262
+ break;
263
+ }
264
+ }
225
265
  }
226
266
  if (label === undefined) {
227
- return undefined;
267
+ return {label: undefined};
228
268
  }
229
269
  let s = '';
230
270
  if (showPrefix && this.reportSet.viewerOptions.showPrefixes) {
231
- s = "(" + this.qname(c).prefix + ") ";
271
+ s = "(" + this.reportSet.taxonomyNamer.fromQName(this.qname(c)).prefix + ") ";
232
272
  }
233
273
  s += label;
234
- return s;
274
+ return { label: s, lang: actualLang };
235
275
  }
236
276
  }
237
277
 
@@ -243,6 +283,14 @@ export class XBRLReport {
243
283
  return label;
244
284
  }
245
285
 
286
+ getLabelOrNameAndLang(c, rolePrefix, showPrefix) {
287
+ const labelLang = this.getLabelAndLang(c, rolePrefix, showPrefix);
288
+ if (labelLang.label === undefined) {
289
+ return { label: c };
290
+ }
291
+ return labelLang;
292
+ }
293
+
246
294
  isCalculationContributor(c) {
247
295
  if (this._calculationContributors === undefined) {
248
296
  if (this._reportData.rels?.calc) {
@@ -270,4 +318,11 @@ export class XBRLReport {
270
318
  }
271
319
  return this._calculationSummations.has(c);
272
320
  }
321
+
322
+ /**
323
+ * @return {Array[String]} Software credit text labels provided with this report for display purposes.
324
+ */
325
+ softwareCredits() {
326
+ return this._reportData.softwareCredits ?? [];
327
+ }
273
328
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { ReportSet } from "./reportset.js";
4
4
  import { ViewerOptions } from "./viewerOptions.js";
5
+ import { TestInspector } from "./test-utils.js";
5
6
  import { NAMESPACE_ISO4217 } from "./util";
6
7
 
7
8
  var testReportData = {
@@ -17,7 +18,11 @@ var testReportData = {
17
18
  "role1": "https://www.example.com/role1",
18
19
  "role2": "https://www.example.com/role2",
19
20
  "role3": "https://www.example.com/role3",
20
- "role4": "https://www.example.com/role4"
21
+ "role4": "https://www.example.com/role4MoreWords",
22
+ "std": "http://www.xbrl.org/2003/role/label",
23
+ "doc": "http://www.xbrl.org/2003/role/documentation",
24
+ "verbose": "http://www.xbrl.org/2003/role/verboseLabel",
25
+ "total": "http://www.xbrl.org/2003/role/totalLabel",
21
26
  },
22
27
  "roleDefs": {
23
28
  "role1": { "en": "Role 1 Label" },
@@ -62,9 +67,15 @@ var testReportData = {
62
67
  "p": "2018-01-01/2019-01-01",
63
68
  }
64
69
  }
65
- }
70
+ },
71
+
72
+ "softwareCredits": ["Example credit text A", "Example credit text B"],
66
73
  };
67
74
 
75
+ var insp = new TestInspector();
76
+ beforeAll(() => {
77
+ return insp.i18nInit();
78
+ });
68
79
 
69
80
  describe("Language options", () => {
70
81
  const testReportSet = new ReportSet(testReportData);
@@ -101,23 +112,33 @@ describe("Concept labels", () => {
101
112
  test("Label fallback", () => {
102
113
  vo.language = 'fr';
103
114
  expect(testReport.getLabel('eg:Concept3', 'std')).toBe("Concept trois");
115
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std').label).toBe("Concept trois");
116
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std').lang).toBe("fr");
104
117
  expect(testReport.getLabelOrName('eg:Concept3', 'std')).toBe("Concept trois");
105
118
  vo.language = 'es';
106
119
  expect(testReport.getLabel('eg:Concept3', 'std')).toBe("Concept cuatro");
120
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std').label).toBe("Concept cuatro");
121
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std').lang).toBe("es");
107
122
  expect(testReport.getLabelOrName('eg:Concept3', 'std')).toBe("Concept cuatro");
108
123
  // No English label, so fall back on German (de is first alphabetically)
109
124
  vo.language = 'en';
110
125
  expect(testReport.getLabel('eg:Concept3', 'std')).toBe("Concept vier");
126
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std').label).toBe("Concept vier");
127
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std').lang).toBe("de");
111
128
  expect(testReport.getLabelOrName('eg:Concept3', 'std')).toBe("Concept vier");
112
129
 
113
130
  // Attempt to get an undefined label type
114
131
  expect(testReport.getLabel('eg:Concept3', 'doc')).toBeUndefined();
132
+ expect(testReport.getLabelAndLang('eg:Concept3', 'doc').label).toBeUndefined();
133
+ expect(testReport.getLabelAndLang('eg:Concept3', 'doc').lang).toBeUndefined();
115
134
  expect(testReport.getLabelOrName('eg:Concept3', 'doc')).toBe('eg:Concept3');
116
135
  });
117
136
 
118
137
  test("With prefix", () => {
119
138
  vo.language = 'fr';
120
139
  expect(testReport.getLabel('eg:Concept3', 'std', true)).toBe("(eg) Concept trois");
140
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std', true).label).toBe("(eg) Concept trois");
141
+ expect(testReport.getLabelAndLang('eg:Concept3', 'std', true).lang).toBe("fr");
121
142
  expect(testReport.getLabelOrName('eg:Concept3', 'std', true)).toBe("(eg) Concept trois");
122
143
 
123
144
  expect(testReport.getLabel('eg:Concept3', 'doc', true)).toBeUndefined();
@@ -131,16 +152,66 @@ describe("ELR labels", () => {
131
152
  testReportSet._initialize();
132
153
  const testReport = testReportSet.reports[0];
133
154
  test("Present", () => {
134
- expect(testReport.getRoleLabel("role1")).toBe("Role 1 Label");
155
+ expect(testReport.getRoleLabelOrURI("role1")).toBe("Role 1 Label");
156
+ });
157
+ test("Null", () => {
158
+ expect(testReport.getRoleLabelOrURI("role2")).toBe("https://www.example.com/role2");
159
+ });
160
+ test("No label", () => {
161
+ expect(testReport.getRoleLabelOrURI("role3")).toBe("https://www.example.com/role3");
162
+ });
163
+ test("Not present in roleDef", () => {
164
+ expect(testReport.getRoleLabelOrURI("role4")).toBe("https://www.example.com/role4MoreWords");
165
+ });
166
+ });
167
+
168
+ describe("Label role labels", () => {
169
+ const testReportSet = new ReportSet(testReportData);
170
+ testReportSet._initialize();
171
+ const testReport = testReportSet.reports[0];
172
+ test("Built-in (via i18n)", () => {
173
+ expect(testReport.getLabelRoleLabel("std")).toBe("Standard Label");
174
+ });
175
+ test("Built-in (via i18n)", () => {
176
+ expect(testReport.getLabelRoleLabel("doc")).toBe("Documentation Label");
177
+ });
178
+ test("Built-in (via de-camelcase)", () => {
179
+ expect(testReport.getLabelRoleLabel("verbose")).toBe("Verbose Label");
180
+ });
181
+ test("Present", () => {
182
+ expect(testReport.getLabelRoleLabel("role1")).toBe("Role 1 Label");
135
183
  });
136
184
  test("Null", () => {
137
- expect(testReport.getRoleLabel("role2")).toBe("https://www.example.com/role2");
185
+ expect(testReport.getLabelRoleLabel("role2")).toBe("Role2");
138
186
  });
139
187
  test("No label", () => {
140
- expect(testReport.getRoleLabel("role3")).toBe("https://www.example.com/role3");
188
+ expect(testReport.getLabelRoleLabel("role3")).toBe("Role3");
141
189
  });
142
190
  test("Not present in roleDef", () => {
143
- expect(testReport.getRoleLabel("role4")).toBe("https://www.example.com/role4");
191
+ expect(testReport.getLabelRoleLabel("role4")).toBe("Role4 More Words");
192
+ });
193
+
194
+ });
195
+
196
+ describe("Fetching software credit", () => {
197
+
198
+ test("Successful", () => {
199
+ const testReportSet = new ReportSet(testReportData);
200
+ testReportSet._initialize();
201
+ const report = testReportSet.reports[0];
202
+
203
+ const softwareCredits = report.softwareCredits();
204
+ expect(softwareCredits).toEqual(["Example credit text A", "Example credit text B"]);
144
205
  });
145
206
 
207
+ test("Unset", () => {
208
+ let alternateData = JSON.parse(JSON.stringify(testReportData));
209
+ delete alternateData.softwareCredits;
210
+ const testReportSet = new ReportSet(alternateData);
211
+ testReportSet._initialize();
212
+ const report = testReportSet.reports[0];
213
+
214
+ const softwareCredits = report.softwareCredits();
215
+ expect(softwareCredits).toEqual([]);
216
+ });
146
217
  });
@@ -7,8 +7,7 @@ import { Unit } from "./unit";
7
7
  import { titleCase, viewerUniqueId } from "./util.js";
8
8
  import { QName } from "./qname.js";
9
9
  import { ViewerOptions } from './viewerOptions.js';
10
- import $ from 'jquery'
11
-
10
+ import { TaxonomyNamer } from './taxonomynamer.js';
12
11
 
13
12
  // Class represents the set of XBRL "target" reports shown in the viewer.
14
13
  // Each contained report represents the data from a single target document in a
@@ -19,6 +18,7 @@ export class ReportSet {
19
18
  this._data = data;
20
19
  this._ixNodeMap = {};
21
20
  this.viewerOptions = new ViewerOptions()
21
+ this.taxonomyNamer = new TaxonomyNamer(new Map());
22
22
  }
23
23
 
24
24
  /*
@@ -99,6 +99,10 @@ export class ReportSet {
99
99
  return this._data.prefixes;
100
100
  }
101
101
 
102
+ preferredPrefix(prefix) {
103
+ return this.taxonomyNamer.getName(prefix, this._data.prefixes[prefix]).prefix;
104
+ }
105
+
102
106
  namespaceGroups() {
103
107
  const counts = {};
104
108
  for (const f of this.facts()) {
@@ -118,6 +122,28 @@ export class ReportSet {
118
122
  return this._usedPrefixes;
119
123
  }
120
124
 
125
+ getUsedConceptDataTypes() {
126
+ if (this._usedDataTypes === undefined) {
127
+ const map = new Map()
128
+ for (const dt of Object.values(this._items)
129
+ .filter(f => f instanceof Fact)
130
+ .map(f => ({ dataType: f.concept().dataType(), isNumeric: f.isNumeric() }))
131
+ .filter(t => t.dataType !== undefined)) {
132
+ map.set(dt.dataType.name, dt);
133
+ }
134
+ this._usedDataTypes = map.values().toArray();
135
+ }
136
+ return this._usedDataTypes;
137
+ }
138
+
139
+ /**
140
+ * @return {Array[String]} Sorted list of unique software credit text values
141
+ */
142
+ getSoftwareCredits() {
143
+ let softwareCredits = new Set(this.reports.flatMap(r => r.softwareCredits()));
144
+ return Array.from(softwareCredits).sort();
145
+ }
146
+
121
147
  getTargetDocuments() {
122
148
  if (this._targetDocuments === undefined) {
123
149
  this._targetDocuments = new Set(Object.values(this._items)
@@ -219,6 +245,10 @@ export class ReportSet {
219
245
  return this.reportsData().some(r => r.rels?.["w-n"] !== undefined);
220
246
  }
221
247
 
248
+ usesCalculations() {
249
+ return this.reportsData().some(r => Object.keys(r.rels?.calc ?? {}).length > 0);
250
+ }
251
+
222
252
  hasValidationErrors() {
223
253
  return this._data.validation !== undefined && this._data.validation.length > 0;
224
254
  }
@@ -230,5 +260,5 @@ export class ReportSet {
230
260
  factsForReport(report) {
231
261
  return Object.values(this._items).filter(i => i instanceof Fact && i.report == report);
232
262
  }
233
-
263
+
234
264
  }
@@ -73,7 +73,10 @@ function multiReportTestData(withAnchoring) {
73
73
  "facts": {
74
74
  ...createNumericFact("f1", "eg:Concept1", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
75
75
  ...createNumericFact("f2", "eg:Concept2", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
76
- }
76
+ },
77
+
78
+
79
+ "softwareCredits": ["Example credit text C", "Example credit text B"],
77
80
  },
78
81
  ]
79
82
  },
@@ -112,7 +115,9 @@ function multiReportTestData(withAnchoring) {
112
115
 
113
116
  "rels": {
114
117
  ...anchoringRelationShips(withAnchoring)
115
- }
118
+ },
119
+
120
+ "softwareCredits": ["Example credit text A"],
116
121
  }
117
122
  ]
118
123
  }
@@ -165,7 +170,9 @@ function singleReportTestData() {
165
170
  "facts": {
166
171
  ...createNumericFact("f1", "eg:Concept1", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
167
172
  ...createNumericFact("f2", "eg:Concept2", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
168
- }
173
+ },
174
+
175
+ "softwareCredits": ["Example credit text"],
169
176
  };
170
177
  }
171
178
 
@@ -233,8 +240,8 @@ describe("Multi report - ELR labels", () => {
233
240
 
234
241
  test("Present", () => {
235
242
  // Role 1 only has a label in report 1
236
- expect(testReportSet.reports[0].getRoleLabel("role1")).toBe("Role 1 Label");
237
- expect(testReportSet.reports[1].getRoleLabel("role1")).toBe("https://www.example.com/role1");
243
+ expect(testReportSet.reports[0].getRoleLabelOrURI("role1")).toBe("Role 1 Label");
244
+ expect(testReportSet.reports[1].getRoleLabelOrURI("role1")).toBe("https://www.example.com/role1");
238
245
  });
239
246
  });
240
247
 
@@ -326,6 +333,25 @@ describe("ELR labels", () => {
326
333
  testReportSet._initialize();
327
334
 
328
335
  test("Present", () => {
329
- expect(testReportSet.reports[0].getRoleLabel("role1")).toBe("Role 1 Label");
336
+ expect(testReportSet.reports[0].getRoleLabelOrURI("role1")).toBe("Role 1 Label");
337
+ });
338
+ });
339
+
340
+ describe("Fetching software credit", () => {
341
+
342
+ test("Single", () => {
343
+ const testReportSet = new ReportSet(singleReportTestData());
344
+ testReportSet._initialize();
345
+
346
+ const softwareCredits = testReportSet.getSoftwareCredits();
347
+ expect(softwareCredits).toEqual(["Example credit text"]);
348
+ });
349
+
350
+ test("Multiple", () => {
351
+ const testReportSet = new ReportSet(multiReportTestData(true));
352
+ testReportSet._initialize();
353
+
354
+ const softwareCredits = testReportSet.getSoftwareCredits();
355
+ expect(softwareCredits).toEqual(["Example credit text A", "Example credit text B", "Example credit text C"]);
330
356
  });
331
357
  });
@@ -1,8 +1,6 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
3
  import lunr from 'lunr'
4
- import $ from 'jquery'
5
- import Decimal from "decimal.js";
6
4
 
7
5
  export class ReportSearch {
8
6
  constructor(reportSet) {
@@ -15,42 +13,49 @@ export class ReportSearch {
15
13
  var dims = {};
16
14
  var facts = this._reportSet.facts();
17
15
  this.periods = {};
18
- for (var i = 0; i < facts.length; i++) {
19
- var f = facts[i];
20
- var doc = { "id": f.vuid };
21
- var l = f.getLabel("std");
22
- doc.concept = f.conceptQName().localname;
23
- doc.doc = f.getLabel("doc");
24
- doc.date = f.periodTo();
25
- doc.startDate = f.periodFrom();
26
- var dims = f.dimensions();
27
- for (var d in dims) {
28
- if (f.report.getConcept(d).isTypedDimension()) {
29
- if (dims[d] !== null) {
30
- l += " " + dims[d];
16
+ // Add hidden facts to index later, so that they appear later in the
17
+ // default search
18
+ for (const hidden of [false, true]) {
19
+ for (var i = 0; i < facts.length; i++) {
20
+ var f = facts[i];
21
+ if (f.isHidden() !== hidden) {
22
+ continue;
23
+ }
24
+ var doc = { "id": f.vuid };
25
+ var l = f.getLabel("std");
26
+ doc.concept = f.conceptQName().localname;
27
+ doc.doc = f.getLabel("doc");
28
+ doc.date = f.periodTo();
29
+ doc.startDate = f.periodFrom();
30
+ var dims = f.dimensions();
31
+ for (var d in dims) {
32
+ if (f.report.getConcept(d).isTypedDimension()) {
33
+ if (dims[d] !== null) {
34
+ l += " " + dims[d];
35
+ }
36
+ }
37
+ else {
38
+ l += " " + f.report.getLabel(dims[d], "std");
31
39
  }
32
40
  }
33
- else {
34
- l += " " + f.report.getLabel(dims[d], "std");
41
+ doc.label = l;
42
+ doc.ref = f.concept().referenceValuesAsString();
43
+ const wider = f.widerConcepts();
44
+ if (wider.length > 0) {
45
+ doc.widerConcept = f.report.qname(wider[0]).localname;
46
+ doc.widerLabel = f.report.getLabel(wider[0], "std");
47
+ doc.widerDoc = f.report.getLabel(wider[0], "doc");
35
48
  }
36
- }
37
- doc.label = l;
38
- doc.ref = f.concept().referenceValuesAsString();
39
- const wider = f.widerConcepts();
40
- if (wider.length > 0) {
41
- doc.widerConcept = f.report.qname(wider[0]).localname;
42
- doc.widerLabel = f.report.getLabel(wider[0], "std");
43
- doc.widerDoc = f.report.getLabel(wider[0], "doc");
44
- }
45
- docs.push(doc);
49
+ docs.push(doc);
46
50
 
47
- var p = f.period();
48
- if (p) {
49
- this.periods[p.key()] = p.toString();
50
- }
51
+ var p = f.period();
52
+ if (p) {
53
+ this.periods[p.key()] = p.toString();
54
+ }
51
55
 
52
- if (i % 100 === 0) {
53
- yield;
56
+ if (i % 100 === 0) {
57
+ yield;
58
+ }
54
59
  }
55
60
  }
56
61
  const builder = new lunr.Builder();
@@ -88,7 +93,11 @@ export class ReportSearch {
88
93
  }
89
94
 
90
95
  visibilityFilter(s, item) {
91
- return item.isHidden() ? s.showHiddenFacts : s.showVisibleFacts;
96
+ return (
97
+ s.visibilityFilter === '*' ||
98
+ s.visibilityFilter === 'visible' && !item.isHidden() ||
99
+ s.visibilityFilter === 'hidden' && item.isHidden()
100
+ )
92
101
  }
93
102
 
94
103
  periodFilter(s, item) {
@@ -140,6 +149,14 @@ export class ReportSearch {
140
149
  );
141
150
  }
142
151
 
152
+ dataTypesFilter(s, item) {
153
+ return (
154
+ s.dataTypesFilter == null ||
155
+ s.dataTypesFilter.length === 0 ||
156
+ s.dataTypesFilter.some(p => item.concept().dataType()?.name === p)
157
+ );
158
+ }
159
+
143
160
  unitsFilter(s, item) {
144
161
  return (
145
162
  s.unitsFilter.length === 0 ||
@@ -161,6 +178,13 @@ export class ReportSearch {
161
178
  );
162
179
  }
163
180
 
181
+ mandatoryFactFilter(s, item) {
182
+ if(!s.showMandatoryFacts) {
183
+ return true;
184
+ }
185
+ return item.isMandatory();
186
+ }
187
+
164
188
  search(s) {
165
189
  if (!this.ready) {
166
190
  return;
@@ -177,9 +201,11 @@ export class ReportSearch {
177
201
  this.factValueFilter,
178
202
  this.calculationsFilter,
179
203
  this.namespacesFilter,
204
+ this.dataTypesFilter,
180
205
  this.unitsFilter,
181
206
  this.scalesFilter,
182
- this.targetDocumentFilter
207
+ this.targetDocumentFilter,
208
+ this.mandatoryFactFilter
183
209
  ];
184
210
 
185
211
  rr.forEach((r,_) => {
@@ -55,7 +55,7 @@ function createDimensionalizedFact(id, concept, options=null, dimensions= {}) {
55
55
 
56
56
  // Returns a report set with a single report
57
57
  function testReport(testData, sourceIXData) {
58
- return testReportSet([ {data: testData, ixData: sourceIXData ?? {} } ])
58
+ return testReportSet([ {data: testData, ixData: sourceIXData ?? { } } ])
59
59
  }
60
60
 
61
61
  function mergeDicts(a, b) {
@@ -71,7 +71,7 @@ function testReportSet(reports) {
71
71
  };
72
72
 
73
73
  const ixData = {};
74
- for (const [n, report] of reports.entries()) {
74
+ for (const report of reports.values()) {
75
75
  const targetReport = {};
76
76
  for (const key of ["languages", "prefixes", "roles"]) {
77
77
  reportSetData[key] = mergeDicts(reportSetData[key], report.data[key]);
@@ -83,12 +83,15 @@ function testReportSet(reports) {
83
83
 
84
84
  for (const id of Object.keys(targetReport.facts)) {
85
85
  if (!(id in ixData)) {
86
- ixData[viewerUniqueId(n,id)] = report.ixData?.[id] ?? {};
86
+ ixData[viewerUniqueId(0, id)] = report.ixData?.[id] ?? {};
87
87
  }
88
88
  }
89
89
  targetReports.push(targetReport);
90
90
  }
91
91
  const reportSet = new ReportSet(reportSetData);
92
+ for (const [id, data] of Object.entries(ixData)) {
93
+ ixData[id].isHidden ??= false;
94
+ }
92
95
  reportSet.setIXNodeMap(ixData);
93
96
  return reportSet;
94
97
  }
@@ -96,13 +99,13 @@ function testReportSet(reports) {
96
99
  function testSearchSpec(searchString='') {
97
100
  const spec = {};
98
101
  spec.searchString = searchString;
99
- spec.showVisibleFacts = true;
100
- spec.showHiddenFacts = true;
102
+ spec.visibilityFilter = '*';
101
103
  spec.namespacesFilter = [];
102
104
  spec.unitsFilter = [];
103
105
  spec.scalesFilter = [];
104
106
  spec.periodFilter = [];
105
107
  spec.conceptTypeFilter = '*';
108
+ spec.dataTypesFilter = [];
106
109
  spec.dimensionTypeFilter = [];
107
110
  spec.factValueFilter = '*';
108
111
  spec.calculationsFilter = [];
@@ -94,13 +94,17 @@ export class DocumentSummary {
94
94
  counter.addDimension(dimension);
95
95
 
96
96
  const dimensionConcept = fact.report.getConcept(dimension);
97
- if (dimensionConcept.isTypedDimension()) {
97
+ if (!dimensionConcept.hasDefinition) {
98
+ console.log("Missing definition for dimension: " + dimension);
99
+ }
100
+ else if (dimensionConcept.isTypedDimension()) {
98
101
  const typedDomainElement = dimensionConcept.typedDomainElement();
99
102
  if (typedDomainElement) {
100
103
  counter = this._getTagCounter(tagCounts, typedDomainElement);
101
104
  counter.addMember(typedDomainElement);
102
105
  }
103
- } else {
106
+ }
107
+ else {
104
108
  counter = this._getTagCounter(tagCounts, member);
105
109
  counter.addMember(member);
106
110
  }
@@ -125,6 +129,20 @@ export class DocumentSummary {
125
129
  return this._totalFacts;
126
130
  }
127
131
 
132
+ hiddenFacts() {
133
+ if (this._hiddenFacts === undefined) {
134
+ this._hiddenFacts = this._reportSet.facts().filter(f => f.isHidden()).length;
135
+ }
136
+ return this._hiddenFacts;
137
+ }
138
+
139
+ mandatoryFacts() {
140
+ if (this._mandatoryFacts === undefined) {
141
+ this._mandatoryFacts = this._reportSet.facts().filter(f => f.isMandatory()).length;
142
+ }
143
+ return this._mandatoryFacts;
144
+ }
145
+
128
146
  tagCounts() {
129
147
  if (this._tagCounts === undefined) {
130
148
  this._buildTagCounts();
@@ -138,4 +156,12 @@ export class DocumentSummary {
138
156
  }
139
157
  return this._localFileSummary.getDocuments()
140
158
  }
159
+
160
+
161
+ /**
162
+ * @return {Array[String]} Report set's list of software credit text values.
163
+ */
164
+ getSoftwareCredits() {
165
+ return this._reportSet.getSoftwareCredits();
166
+ }
141
167
  }