ixbrl-viewer 1.4.21__py3-none-any.whl → 1.4.49__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 +202 -131
  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 +27 -2
  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 +22 -0
  69. iXBRLViewerPlugin/viewer/src/js/summary.test.js +50 -13
  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.49.dist-info}/METADATA +41 -14
  102. ixbrl_viewer-1.4.49.dist-info/RECORD +197 -0
  103. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.49.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 +28 -0
  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.49.dist-info}/LICENSE +0 -0
  112. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.49.dist-info}/NOTICE +0 -0
  113. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.49.dist-info}/entry_points.txt +0 -0
  114. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.49.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,10 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
- import $ from 'jquery';
4
3
  import Decimal from 'decimal.js';
5
4
  import { setDefault } from './util.js';
6
5
  import { Interval } from './interval.js';
7
6
  import { FactSet } from './factset.js';
7
+ import { CALC11_ARCROLE, CALC_ARCROLE } from './util.js';
8
8
 
9
9
  export class Calculation {
10
10
 
@@ -19,13 +19,16 @@ export class Calculation {
19
19
  const fact = this.fact;
20
20
  const report = fact.report;
21
21
  if (!this._conceptToFact) {
22
- const rels = report.getChildRelationships(fact.conceptName(), "calc")
23
22
  const ctf = {};
24
- for (const [elr, rr] of Object.entries(rels)) {
25
- ctf[elr] = {};
26
- if (rr.length > 0) {
27
- const otherFacts = report.getAlignedFacts(fact, {"c": rr.map(r => r.t )});
28
- otherFacts.forEach(otherFact => setDefault(ctf[elr], otherFact.conceptName(), new FactSet()).add(otherFact));
23
+ for (const version of [CALC_ARCROLE, CALC11_ARCROLE]) {
24
+ const rels = report.getChildRelationships(fact.conceptName(), version)
25
+ for (const [elr, rr] of Object.entries(rels)) {
26
+ setDefault(ctf, version, {});
27
+ ctf[version][elr] = {};
28
+ if (rr.length > 0) {
29
+ const otherFacts = report.getAlignedFacts(fact, {"c": rr.map(r => r.t )});
30
+ otherFacts.forEach(otherFact => setDefault(ctf[version][elr], otherFact.conceptName(), new FactSet()).add(otherFact));
31
+ }
29
32
  }
30
33
  }
31
34
  this._conceptToFact = ctf;
@@ -41,11 +44,13 @@ export class Calculation {
41
44
  resolvedCalculations() {
42
45
  const calculations = [];
43
46
  const ctf = this.calculationFacts();
44
- for (const [elr, concepts] of Object.entries(ctf)) {
45
- if (Object.keys(concepts).length > 0) {
46
- calculations.push(this.resolvedCalculation(elr));
47
- }
48
- }
47
+ for (const [version, o] of Object.entries(ctf)) {
48
+ for (const [elr, concepts] of Object.entries(o)) {
49
+ if (Object.keys(concepts).length > 0) {
50
+ calculations.push(this.resolvedCalculation(elr, version));
51
+ }
52
+ }
53
+ }
49
54
  return calculations;
50
55
  }
51
56
 
@@ -56,35 +61,36 @@ export class Calculation {
56
61
  const ctf = this.calculationFacts();
57
62
  let bestMatchELR = "";
58
63
  let bestMatchCount = -1;
59
- for (const [elr, rr] of Object.entries(ctf)) {
60
- let matchCount = 0;
61
- for (const [concept, calcFactSet] of Object.entries(rr)) {
62
- let matched = 0;
63
- for (const calcFact of calcFactSet.items()) {
64
- if (facts.includes(calcFact.vuid)) {
65
- matched = 1;
66
- }
64
+ for (const [version, o] of Object.entries(ctf)) {
65
+ for (const [elr, rr] of Object.entries(o)) {
66
+ let matchCount = 0;
67
+ for (const [concept, calcFactSet] of Object.entries(rr)) {
68
+ let matched = 0;
69
+ for (const calcFact of calcFactSet.items()) {
70
+ if (facts.includes(calcFact.vuid)) {
71
+ matched = 1;
72
+ }
73
+ }
74
+ matchCount += matched;
67
75
  }
68
- matchCount += matched;
69
- }
70
- if (matchCount/Object.keys(rr).length > bestMatchCount) {
71
- bestMatchCount = matchCount/Object.keys(rr).length;
72
- bestMatchELR = elr;
73
- }
74
- }
76
+ if (matchCount/Object.keys(rr).length > bestMatchCount) {
77
+ bestMatchCount = matchCount/Object.keys(rr).length;
78
+ bestMatchELR = elr;
79
+ }
80
+ }
81
+ }
75
82
  return bestMatchELR;
76
83
  }
77
84
 
78
85
  /*
79
86
  * Returns a ResolvedCalculation object for the specified ELR
80
87
  */
81
- resolvedCalculation(elr) {
82
- const calc = [];
83
- const calcFacts = this.calculationFacts()[elr];
88
+ resolvedCalculation(elr, version) {
89
+ const calcFacts = this.calculationFacts()[version][elr];
84
90
  const report = this.fact.report;
85
- const rels = report.getChildRelationships(this.fact.conceptName(), "calc")[elr];
91
+ const rels = report.getChildRelationships(this.fact.conceptName(), version)[elr];
86
92
  const resolvedCalcClass = this.calc11 ? ResolvedCalc11Calculation : ResolvedLegacyCalculation;
87
- const resolvedCalculation = new resolvedCalcClass(elr, this.fact);
93
+ const resolvedCalculation = new resolvedCalcClass(elr, this.fact, version);
88
94
  for (const r of rels) {
89
95
  const factset = calcFacts[r.t] ?? new FactSet();
90
96
  resolvedCalculation.addRow(new CalculationContribution(report.getConcept(r.t), r.w, factset));
@@ -122,11 +128,12 @@ class CalculationContribution {
122
128
 
123
129
  class AbstractResolvedCalculation {
124
130
 
125
- constructor(elr, fact) {
131
+ constructor(elr, fact, relationshipVersion) {
126
132
  this.totalFact = fact;
127
133
  this.totalFactSet = new FactSet(fact.report.getAlignedFacts(fact));
128
134
  this.elr = elr;
129
135
  this.rows = [];
136
+ this.relationshipVersion = relationshipVersion;
130
137
  }
131
138
 
132
139
  addRow(contribution) {
@@ -173,13 +180,17 @@ export class ResolvedCalc11Calculation extends AbstractResolvedCalculation {
173
180
  export class ResolvedLegacyCalculation extends AbstractResolvedCalculation {
174
181
 
175
182
  binds() {
176
- return super.binds() && this.rows.every((r) => r.facts.completeDuplicates());
183
+ return this.relationshipVersion == CALC_ARCROLE && super.binds() && this.rows.every((r) => r.facts.completeDuplicates());
177
184
  }
178
185
 
179
186
  unchecked() {
180
187
  return super.binds() && !this.binds();
181
188
  }
182
189
 
190
+ uncheckedDueToVersionMismatch() {
191
+ return this.relationshipVersion !== CALC_ARCROLE;
192
+ }
193
+
183
194
  calculatedTotal() {
184
195
  let total = new Decimal(0);
185
196
 
@@ -16,7 +16,7 @@ import $ from 'jquery';
16
16
  import i18next from 'i18next';
17
17
 
18
18
  import { Dialog } from './dialog.js';
19
- import { ResolvedCalc11Calculation, ResolvedLegacyCalculation } from './calculation.js';
19
+ import { ResolvedCalc11Calculation } from './calculation.js';
20
20
 
21
21
  export class CalculationInspector extends Dialog {
22
22
  constructor() {
@@ -112,8 +112,6 @@ export class CalculationInspector extends Dialog {
112
112
  let hasIcons = false;
113
113
  for (const row of resolvedCalculation.rows) {
114
114
  let factText = "";
115
- let minText = "";
116
- let maxText = "";
117
115
 
118
116
  if (!row.facts.isEmpty()) {
119
117
  let f = row.facts.items()[0];
@@ -162,7 +160,6 @@ export class CalculationInspector extends Dialog {
162
160
  const tbody = table.find("tbody");
163
161
  // We remove the padding on the icons column (used to indicate
164
162
  // duplicate facts) if it is empty to avoid an unsightly gap
165
- var hasIcons = false;
166
163
  tbody.empty();
167
164
  table.removeClass("has-icons");
168
165
  if (resolvedCalculation instanceof ResolvedCalc11Calculation) {
@@ -176,13 +173,13 @@ export class CalculationInspector extends Dialog {
176
173
  const messageCell = table.find("td.status");
177
174
  messageCell.removeClass("inconsistent").removeClass("consistent").removeClass("unchecked");
178
175
  if (resolvedCalculation.unchecked()) {
179
- messageCell.addClass("unchecked").find(".message").text("Calculation does not bind");
176
+ messageCell.addClass("unchecked").find(".message").text(i18next.t('calculation.does-not-bind'));
180
177
  }
181
178
  else if (resolvedCalculation.isConsistent()) {
182
- messageCell.addClass("consistent").find(".message").text("Calculation is consistent");
179
+ messageCell.addClass("consistent").find(".message").text(i18next.t('factDetails.calculationIsConsistent'));
183
180
  }
184
181
  else {
185
- messageCell.addClass("inconsistent").find(".message").text("Calculation is inconsistent");
182
+ messageCell.addClass("inconsistent").find(".message").text(i18next.t('factDetails.calculationIsInconsistent'));
186
183
  }
187
184
  }
188
185
  }
@@ -1,14 +1,21 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
3
  import $ from 'jquery';
4
- import Chart from 'chart.js';
4
+ import { BarController, BarElement, CategoryScale, Chart, LinearScale } from 'chart.js';
5
5
  import { AspectSet } from './aspect.js';
6
6
  import { wrapLabel } from "./util.js";
7
7
  import { Dialog } from './dialog.js';
8
+ import { getVariable } from './theme.js';
9
+
8
10
 
9
11
  export class IXBRLChart extends Dialog {
10
12
  constructor() {
11
13
  super(".dialog.chart");
14
+ this.dataSetColours = [
15
+ getVariable('--colour-primary'),
16
+ getVariable('--colour-secondary'),
17
+ getVariable('--colour-warning'),
18
+ ]
12
19
  }
13
20
 
14
21
  _multiplierDescription(m) {
@@ -32,11 +39,7 @@ export class IXBRLChart extends Dialog {
32
39
  }
33
40
 
34
41
  dataSetColour(i) {
35
- return [
36
- '#66cc00',
37
- '#0094ff',
38
- '#fbad17'
39
- ][i];
42
+ return this.dataSetColours[i];
40
43
  }
41
44
 
42
45
  addAspect(a) {
@@ -139,13 +142,13 @@ export class IXBRLChart extends Dialog {
139
142
  for (const av of fact.aspects()) {
140
143
  /* Don't show concept in list of additional aspects */
141
144
  if (av.name() != 'c') {
142
- const a = $("<div>")
145
+ const a = $("<button>")
143
146
  .addClass("other-aspect")
144
147
  .appendTo($(".other-aspects",c));
145
148
  if (dims.includes(av.name())) {
146
149
  a.addClass("selected")
147
150
  .text(av.label() + ": *")
148
- .click(() => this.removeAspect(av.name()));
151
+ .on("click", () => this.removeAspect(av.name()));
149
152
  }
150
153
  else {
151
154
  if (av.name() != 'u') {
@@ -154,7 +157,7 @@ export class IXBRLChart extends Dialog {
154
157
  a.text(av.label() + ": " + av.valueLabel());
155
158
  if (dims.length < 2) {
156
159
  a.addClass("addable")
157
- .click(() => this.addAspect(av.name()));
160
+ .on("click", () => this.addAspect(av.name()));
158
161
  }
159
162
  }
160
163
  }
@@ -173,6 +176,7 @@ export class IXBRLChart extends Dialog {
173
176
  this.setChartSize();
174
177
 
175
178
  const ctx = $("canvas", c);
179
+ Chart.register(BarController, BarElement, CategoryScale, LinearScale);
176
180
  const chart = new Chart(ctx, {
177
181
  type: "bar",
178
182
  data: {
@@ -183,29 +187,27 @@ export class IXBRLChart extends Dialog {
183
187
  responsive: true,
184
188
  maintainAspectRatio: false,
185
189
  scales: {
186
- yAxes: [{
187
- ticks: {
188
- beginAtZero:true
189
- },
190
- scaleLabel: {
190
+ y: {
191
+ beginAtZero: true,
192
+ title: {
191
193
  display: true,
192
- labelString: yLabel,
193
- }
194
-
195
- }],
196
- xAxes: [{
194
+ text: yLabel,
195
+ },
196
+ },
197
+ x: {
197
198
  ticks: {
198
199
  autoSkip: false
199
200
  }
200
- }]
201
+ }
201
202
  }
202
203
 
203
204
  }
204
205
  });
205
- $(window).resize(() => {
206
+ $(window).on("resize", () => {
206
207
  this.setChartSize();
207
208
  chart.resize();
208
209
  });
210
+ $(".other-aspect", c).get(0)?.focus();
209
211
  }
210
212
 
211
213
  setChartSize() {
@@ -1,6 +1,7 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
- import $ from 'jquery'
3
+ import { DataType } from "./datatype.js";
4
+ import { Balance } from "./balance.js";
4
5
 
5
6
  export class Concept {
6
7
  constructor(report, name) {
@@ -24,6 +25,18 @@ export class Concept {
24
25
  }
25
26
  }
26
27
 
28
+ labels() {
29
+ if (!this._c.labels) {
30
+ return {};
31
+ }
32
+ const lang = this.report.reportSet.viewerOptions.language;
33
+ return Object.fromEntries(
34
+ Object.entries(this._c.labels)
35
+ .map(([role, labels]) => [role, labels[lang]])
36
+ .filter(([role, label]) => label !== undefined)
37
+ )
38
+ }
39
+
27
40
  references() {
28
41
  if (!this._c.r) {
29
42
  return [];
@@ -62,6 +75,18 @@ export class Concept {
62
75
  }
63
76
 
64
77
  typedDomainElement() {
65
- return this._c.td
78
+ return this._c.td;
79
+ }
80
+
81
+ dataType() {
82
+ if (this._c.dt !== undefined) {
83
+ return new DataType(this.report, this._c.dt);
84
+ }
85
+ }
86
+
87
+ balance() {
88
+ if (this._c.b !== undefined) {
89
+ return new Balance(this._c.b);
90
+ }
66
91
  }
67
92
  }
@@ -2,6 +2,12 @@
2
2
 
3
3
  import { ReportSet } from "./reportset.js";
4
4
  import { NAMESPACE_ISO4217 } from "./util";
5
+ import { TestInspector } from "./test-utils.js";
6
+
7
+ const insp = new TestInspector();
8
+ beforeAll(() => {
9
+ insp.i18nInit();
10
+ });
5
11
 
6
12
  const testReportData = {
7
13
  "prefixes": {
@@ -15,14 +21,16 @@ const testReportData = {
15
21
  "std": {
16
22
  "en": "English label"
17
23
  }
18
- }
24
+ },
25
+ "b": "debit"
19
26
  },
20
27
  "eg:Concept2": {
21
28
  "labels": {
22
29
  "std": {
23
30
  "en": "English label for concept two"
24
31
  }
25
- }
32
+ },
33
+ "b": "credit"
26
34
  },
27
35
  "eg:Concept3": {
28
36
  "labels": {
@@ -86,3 +94,16 @@ describe("Concept references", () => {
86
94
  ]);
87
95
  });
88
96
  });
97
+
98
+ describe("Balance types", () => {
99
+ test("Debit/Credit", () => {
100
+ const rs = new ReportSet(testReportData);
101
+ rs._initialize();
102
+ const r = rs.reports[0];
103
+ expect(r.getConcept("eg:Concept1").balance().balance).toBe("debit");
104
+ expect(r.getConcept("eg:Concept1").balance().label()).toBe("Debit");
105
+ expect(r.getConcept("eg:Concept2").balance().balance).toBe("credit");
106
+ expect(r.getConcept("eg:Concept2").balance().label()).toBe("Credit");
107
+ expect(r.getConcept("eg:Concept3").balance()).toBeUndefined();
108
+ });
109
+ });
@@ -0,0 +1,20 @@
1
+ // See COPYRIGHT.md for copyright information
2
+
3
+ import i18next from 'i18next';
4
+ import { NAMESPACE_XBRLI } from "./util.js";
5
+
6
+ export class DataType {
7
+ constructor(report, name) {
8
+ this.name = name;
9
+ this.report = report;
10
+ this.qname = report.qname(name);
11
+ }
12
+
13
+ label() {
14
+ if (this.qname.namespace == NAMESPACE_XBRLI) {
15
+ return i18next.t(`dataTypes:${this.qname.localname}`, {defaultValue: this.qname.qname});
16
+ }
17
+ return this.report.reportSet.taxonomyNamer.convertQName(this.qname);
18
+ }
19
+
20
+ }
@@ -0,0 +1,62 @@
1
+ // See COPYRIGHT.md for copyright information
2
+
3
+ import { ReportSet } from "./reportset.js";
4
+ import { NAMESPACE_ISO4217, NAMESPACE_XBRLI } from "./util";
5
+ import { TestInspector } from "./test-utils.js";
6
+
7
+ const testReportData = {
8
+ "prefixes": {
9
+ "eg": "http://www.example.com",
10
+ "iso4217": NAMESPACE_ISO4217,
11
+ "xbrli": NAMESPACE_XBRLI,
12
+ "e": "http://example.com/entity",
13
+ },
14
+ "concepts": {
15
+ },
16
+ "facts": {
17
+ }
18
+ }
19
+
20
+ function testReport(concept) {
21
+ // Deep copy of standing data
22
+ const data = JSON.parse(JSON.stringify(testReportData));
23
+ data.concepts["eg:concept"] = concept;
24
+ const reportSet = new ReportSet(data);
25
+ reportSet.setIXNodeMap({});
26
+ return reportSet.reports[0];
27
+ }
28
+
29
+ const insp = new TestInspector();
30
+ beforeAll(() => {
31
+ insp.i18nInit();
32
+ });
33
+
34
+ describe("Data types", () => {
35
+ test("Monetary", () => {
36
+ const r1 = testReport({ "dt": "xbrli:monetaryItemType" });
37
+ expect(r1.getConcept("eg:concept").dataType().label()).toBe("Monetary");
38
+ });
39
+
40
+ test("String", () => {
41
+ const r1 = testReport({ "dt": "xbrli:stringItemType" });
42
+ expect(r1.getConcept("eg:concept").dataType().label()).toBe("String");
43
+ });
44
+
45
+ test("Unknown xbrli", () => {
46
+ const r1 = testReport({ "dt": "xbrli:unknown" });
47
+ expect(r1.getConcept("eg:concept").dataType().label()).toBe("xbrli:unknown");
48
+ });
49
+
50
+ test("Custom", () => {
51
+ const r1 = testReport({ "dt": "eg:stringItemType" });
52
+ expect(r1.getConcept("eg:concept").dataType().label()).toBe("eg:stringItemType");
53
+ });
54
+
55
+ test("No datatype", () => {
56
+ const r1 = testReport({});
57
+ expect(r1.getConcept("eg:concept").dataType()).toBeUndefined();
58
+ });
59
+ })
60
+
61
+
62
+
@@ -6,9 +6,9 @@ export class Dialog {
6
6
  constructor(selector) {
7
7
  this.node = $("#dialog-templates").find(selector).clone().appendTo("#ixv #dialog-container");
8
8
 
9
- $('.close', this.node).click(() => this.close());
10
- $(document).bind("keyup", (e) => {
11
- if (e.keyCode === 27) {
9
+ $('.close', this.node).on("click", () => this.close());
10
+ $(document).on("keyup", (e) => {
11
+ if (e.which === 27) {
12
12
  this.close();
13
13
  }
14
14
  });
@@ -28,11 +28,13 @@ export class Dialog {
28
28
 
29
29
  close() {
30
30
  $('.dialog-mask').hide();
31
- this.node.remove() ;
31
+ this.node.get(0).close();
32
+ this.node.remove();
32
33
  }
33
34
 
34
35
  show() {
35
36
  $('.dialog-mask').show();
37
+ this.node.get(0).showModal();
36
38
  this.node.show();
37
39
  }
38
40
  }
@@ -2,8 +2,6 @@
2
2
 
3
3
  import $ from 'jquery'
4
4
  import i18next from "i18next";
5
- import { isodateToHuman } from "./util.js"
6
- import { QName } from "./qname.js"
7
5
  import { Aspect } from "./aspect.js";
8
6
  import { Period } from './period.js';
9
7
  import { formatNumber, localId } from "./util.js";
@@ -25,18 +23,34 @@ export class Fact {
25
23
  return localId(this.vuid);
26
24
  }
27
25
 
26
+ isMandatory() {
27
+ return this.f.a.m
28
+ }
29
+
28
30
  getLabel(rolePrefix, withPrefix) {
29
31
  return this.report.getLabel(this.f.a.c, rolePrefix, withPrefix);
30
32
  }
31
33
 
34
+ getLabelAndLang(rolePrefix, withPrefix) {
35
+ return this.report.getLabelAndLang(this.f.a.c, rolePrefix, withPrefix);
36
+ }
37
+
32
38
  getLabelOrName(rolePrefix, withPrefix) {
33
39
  return this.report.getLabelOrName(this.f.a.c, rolePrefix, withPrefix);
34
40
  }
35
41
 
42
+ getLabelOrNameAndLang(rolePrefix, withPrefix) {
43
+ return this.report.getLabelOrNameAndLang(this.f.a.c, rolePrefix, withPrefix);
44
+ }
45
+
36
46
  conceptName() {
37
47
  return this.f.a.c;
38
48
  }
39
49
 
50
+ conceptDisplayName() {
51
+ return this.report.reportSet.taxonomyNamer.convertQName(this.conceptQName());
52
+ }
53
+
40
54
  concept() {
41
55
  return this.report.getConcept(this.f.a.c);
42
56
  }
@@ -67,9 +81,16 @@ export class Fact {
67
81
  }
68
82
 
69
83
  readableValue(val) {
84
+ return this.readableValueHTML(val).textContent;
85
+ }
86
+
87
+ readableValueHTML(val) {
70
88
  let v = val === undefined ? this.f.v : val;
89
+ const span = document.createElement("span");
90
+ span.classList.add("fact-value");
71
91
  if (this.isInvalidIXValue()) {
72
- v = "Invalid value";
92
+ span.classList.add("fact-value-invalid");
93
+ span.append(document.createTextNode("Invalid value"));
73
94
  }
74
95
  else if (this.isNumeric()) {
75
96
  const d = this.decimals();
@@ -81,14 +102,17 @@ export class Fact {
81
102
  formattedNumber = formatNumber(v, d);
82
103
  }
83
104
  if (this.isMonetaryValue()) {
84
- v = this.unitLabel() + " " + formattedNumber;
105
+ span.append(this.unitLabelHTML());
106
+ span.append(document.createTextNode(" " + formattedNumber));
85
107
  }
86
108
  else {
87
- v = formattedNumber + " " + this.unitLabel();
109
+ span.append(document.createTextNode(formattedNumber + " "));
110
+ span.append(this.unitLabelHTML());
88
111
  }
89
112
  }
90
113
  else if (this.isNil()) {
91
- v = "nil";
114
+ span.classList.add("fact-value-nil");
115
+ span.append(document.createTextNode("nil"));
92
116
  }
93
117
  else if (this.isTextBlock()) {
94
118
  const html = $("<div>").append($($.parseHTML(v, null, false)));
@@ -100,6 +124,7 @@ export class Fact {
100
124
  .prepend(document.createTextNode(' '));
101
125
  /* Replace runs of whitespace (including nbsp) with a single space */
102
126
  v = html.text().replace(/[\u00a0\s]+/g, " ").trim();
127
+ span.append(document.createTextNode(v));
103
128
  }
104
129
  else if (this.isEnumeration()) {
105
130
  const labels = [];
@@ -107,8 +132,12 @@ export class Fact {
107
132
  labels.push(this.report.getLabelOrName(qn, 'std'));
108
133
  }
109
134
  v = labels.join(', ');
135
+ span.append(document.createTextNode(v));
110
136
  }
111
- return v;
137
+ else {
138
+ span.append(document.createTextNode(v));
139
+ }
140
+ return span;
112
141
  }
113
142
 
114
143
  /**
@@ -134,6 +163,10 @@ export class Fact {
134
163
  return this.unit()?.label() ?? i18next.t("factDetails.noUnit");
135
164
  }
136
165
 
166
+ unitLabelHTML() {
167
+ return this.unit()?.html() ?? document.createTextNode(i18next.t("factDetails.noUnit"));
168
+ }
169
+
137
170
  getConceptPrefix() {
138
171
  return this.conceptName().split(':')[0];
139
172
  }
@@ -690,10 +690,13 @@ describe("Get Label", () => {
690
690
 
691
691
  test("Get standard label", () => {
692
692
  expect(f.getLabel("std")).toEqual("English label")
693
+ expect(f.getLabelAndLang("std").label).toEqual("English label")
694
+ expect(f.getLabelAndLang("std").lang).toEqual("en")
693
695
  });
694
696
 
695
697
  test("Get non-existent label", () => {
696
698
  expect(f.getLabel("doc")).toBeUndefined();
699
+ expect(f.getLabelAndLang("doc").label).toBeUndefined();
697
700
  });
698
701
 
699
702
  });
@@ -3,9 +3,17 @@
3
3
  import $ from 'jquery'
4
4
  import { iXBRLViewer } from "./ixbrlviewer.js";
5
5
 
6
+ const scriptSrc = document.currentScript.src;
7
+
6
8
  $(() => {
7
- const iv = new iXBRLViewer({
8
- showValidationWarningOnStart: true
9
- });
9
+ const options = {
10
+ showValidationWarningOnStart: true,
11
+ };
12
+ const configUrl = new URL("ixbrlviewer.config.json", scriptSrc);
13
+ if (configUrl.protocol != 'file:') {
14
+ options["configUrl"] = configUrl;
15
+ }
16
+
17
+ const iv = new iXBRLViewer(options);
10
18
  iv.load();
11
19
  });