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.
- iXBRLViewerPlugin/__init__.py +77 -49
- iXBRLViewerPlugin/_version.py +2 -2
- iXBRLViewerPlugin/constants.py +86 -1
- iXBRLViewerPlugin/featureConfig.py +4 -1
- iXBRLViewerPlugin/iXBRLViewer.py +210 -137
- iXBRLViewerPlugin/plugin.py +7 -0
- iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
- iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt +9 -2
- iXBRLViewerPlugin/viewer/i18next-parser.config.js +1 -1
- iXBRLViewerPlugin/viewer/src/data/utr.json +1 -0
- iXBRLViewerPlugin/viewer/src/html/fact-details.html +69 -38
- iXBRLViewerPlugin/viewer/src/html/footer-logo.html +4 -0
- iXBRLViewerPlugin/viewer/src/html/footnote-details.html +1 -1
- iXBRLViewerPlugin/viewer/src/html/inspector.html +324 -211
- iXBRLViewerPlugin/viewer/src/i18n/cy/balancetypes.json +1 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/currencies.json +13 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/datatypes.json +9 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/referenceparts.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/scale.json +16 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/translation.json +179 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/datatypes.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/labelroles.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/scale.json +16 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +56 -24
- iXBRLViewerPlugin/viewer/src/i18n/es/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/datatypes.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/scale.json +16 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +70 -37
- iXBRLViewerPlugin/viewer/src/icons/dark-mode.svg +4 -0
- iXBRLViewerPlugin/viewer/src/img/arelle-dark.svg +179 -0
- iXBRLViewerPlugin/viewer/src/img/inline-viewer-dark.svg +59 -0
- iXBRLViewerPlugin/viewer/src/js/accordian.js +3 -2
- iXBRLViewerPlugin/viewer/src/js/aspect.js +18 -10
- iXBRLViewerPlugin/viewer/src/js/aspect.test.js +2 -2
- iXBRLViewerPlugin/viewer/src/js/balance.js +14 -0
- iXBRLViewerPlugin/viewer/src/js/calculation.js +45 -34
- iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +4 -7
- iXBRLViewerPlugin/viewer/src/js/chart.js +23 -21
- iXBRLViewerPlugin/viewer/src/js/concept.js +30 -3
- iXBRLViewerPlugin/viewer/src/js/concept.test.js +23 -2
- iXBRLViewerPlugin/viewer/src/js/datatype.js +20 -0
- iXBRLViewerPlugin/viewer/src/js/datatype.test.js +62 -0
- iXBRLViewerPlugin/viewer/src/js/dialog.js +6 -4
- iXBRLViewerPlugin/viewer/src/js/fact.js +40 -7
- iXBRLViewerPlugin/viewer/src/js/fact.test.js +3 -0
- iXBRLViewerPlugin/viewer/src/js/index.js +11 -3
- iXBRLViewerPlugin/viewer/src/js/inspector.js +560 -160
- iXBRLViewerPlugin/viewer/src/js/inspector.test.js +1 -2
- iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.js +129 -31
- iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.test.js +133 -20
- iXBRLViewerPlugin/viewer/src/js/ixnode.js +1 -1
- iXBRLViewerPlugin/viewer/src/js/menu.js +25 -7
- iXBRLViewerPlugin/viewer/src/js/number-matcher.js +2 -2
- iXBRLViewerPlugin/viewer/src/js/outline.js +2 -4
- iXBRLViewerPlugin/viewer/src/js/period.js +0 -1
- iXBRLViewerPlugin/viewer/src/js/report.js +68 -13
- iXBRLViewerPlugin/viewer/src/js/report.test.js +77 -6
- iXBRLViewerPlugin/viewer/src/js/reportset.js +33 -3
- iXBRLViewerPlugin/viewer/src/js/reportset.test.js +32 -6
- iXBRLViewerPlugin/viewer/src/js/search.js +61 -35
- iXBRLViewerPlugin/viewer/src/js/search.test.js +8 -5
- iXBRLViewerPlugin/viewer/src/js/summary.js +28 -2
- iXBRLViewerPlugin/viewer/src/js/summary.test.js +52 -14
- iXBRLViewerPlugin/viewer/src/js/tableExport.js +3 -3
- iXBRLViewerPlugin/viewer/src/js/taxonomynamer.js +34 -0
- iXBRLViewerPlugin/viewer/src/js/taxonomynamer.test.js +32 -0
- iXBRLViewerPlugin/viewer/src/js/theme.js +49 -0
- iXBRLViewerPlugin/viewer/src/js/unit.js +73 -2
- iXBRLViewerPlugin/viewer/src/js/unit.test.js +14 -3
- iXBRLViewerPlugin/viewer/src/js/util.js +21 -18
- iXBRLViewerPlugin/viewer/src/js/util.test.js +1 -0
- iXBRLViewerPlugin/viewer/src/js/utr.js +27 -0
- iXBRLViewerPlugin/viewer/src/js/viewer.js +40 -29
- iXBRLViewerPlugin/viewer/src/js/viewerOptions.js +0 -2
- iXBRLViewerPlugin/viewer/src/less/accordian.less +8 -4
- iXBRLViewerPlugin/viewer/src/less/block-list.less +12 -6
- iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +2 -2
- iXBRLViewerPlugin/viewer/src/less/chart.less +8 -5
- iXBRLViewerPlugin/viewer/src/less/colours-dark-mode.less +40 -0
- iXBRLViewerPlugin/viewer/src/less/colours.less +28 -21
- iXBRLViewerPlugin/viewer/src/less/common.less +1 -1
- iXBRLViewerPlugin/viewer/src/less/components.less +3 -3
- iXBRLViewerPlugin/viewer/src/less/core.less +2 -0
- iXBRLViewerPlugin/viewer/src/less/dialog.less +13 -10
- iXBRLViewerPlugin/viewer/src/less/form-controls.less +33 -11
- iXBRLViewerPlugin/viewer/src/less/inspector.less +556 -300
- iXBRLViewerPlugin/viewer/src/less/loader.less +2 -2
- iXBRLViewerPlugin/viewer/src/less/menu.less +33 -15
- iXBRLViewerPlugin/viewer/src/less/summary.less +16 -6
- iXBRLViewerPlugin/viewer/src/less/tabs.less +5 -5
- iXBRLViewerPlugin/viewer/src/less/text-mixins.less +2 -1
- iXBRLViewerPlugin/viewer/src/less/validation-report.less +1 -1
- iXBRLViewerPlugin/viewer/src/less/viewer.less +30 -18
- iXBRLViewerPlugin/viewer/webpack.common.js +19 -9
- {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/METADATA +41 -14
- ixbrl_viewer-1.4.50.dist-info/RECORD +197 -0
- {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/WHEEL +1 -1
- tests/puppeteer/framework/page_objects/doc_frame.js +3 -3
- tests/puppeteer/framework/page_objects/fact_details_panel.js +29 -1
- tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
- tests/puppeteer/tests/fact_properties.test.js +10 -4
- tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +117 -51
- iXBRLViewerPlugin/viewer/src/js/interact.min.js +0 -6
- ixbrl_viewer-1.4.21.dist-info/RECORD +0 -166
- {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/LICENSE +0 -0
- {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/NOTICE +0 -0
- {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/entry_points.txt +0 -0
- {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.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 [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 [
|
|
45
|
-
|
|
46
|
-
|
|
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 [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
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(),
|
|
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
|
|
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(
|
|
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(
|
|
179
|
+
messageCell.addClass("consistent").find(".message").text(i18next.t('factDetails.calculationIsConsistent'));
|
|
183
180
|
}
|
|
184
181
|
else {
|
|
185
|
-
messageCell.addClass("inconsistent").find(".message").text(
|
|
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 = $("<
|
|
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(
|
|
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(
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
},
|
|
190
|
-
scaleLabel: {
|
|
190
|
+
y: {
|
|
191
|
+
beginAtZero: true,
|
|
192
|
+
title: {
|
|
191
193
|
display: true,
|
|
192
|
-
|
|
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,10 +1,13 @@
|
|
|
1
1
|
// See COPYRIGHT.md for copyright information
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { DataType } from "./datatype.js";
|
|
4
|
+
import { Balance } from "./balance.js";
|
|
4
5
|
|
|
5
6
|
export class Concept {
|
|
6
7
|
constructor(report, name) {
|
|
7
|
-
|
|
8
|
+
const c = report.concepts()[name];
|
|
9
|
+
this.hasDefinition = (c !== undefined);
|
|
10
|
+
this._c = c ?? {};
|
|
8
11
|
this.name = name;
|
|
9
12
|
this.report = report;
|
|
10
13
|
}
|
|
@@ -24,6 +27,18 @@ export class Concept {
|
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
labels() {
|
|
31
|
+
if (!this._c.labels) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
const lang = this.report.reportSet.viewerOptions.language;
|
|
35
|
+
return Object.fromEntries(
|
|
36
|
+
Object.entries(this._c.labels)
|
|
37
|
+
.map(([role, labels]) => [role, labels[lang]])
|
|
38
|
+
.filter(([role, label]) => label !== undefined)
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
references() {
|
|
28
43
|
if (!this._c.r) {
|
|
29
44
|
return [];
|
|
@@ -62,6 +77,18 @@ export class Concept {
|
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
typedDomainElement() {
|
|
65
|
-
return this._c.td
|
|
80
|
+
return this._c.td;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
dataType() {
|
|
84
|
+
if (this._c.dt !== undefined) {
|
|
85
|
+
return new DataType(this.report, this._c.dt);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
balance() {
|
|
90
|
+
if (this._c.b !== undefined) {
|
|
91
|
+
return new Balance(this._c.b);
|
|
92
|
+
}
|
|
66
93
|
}
|
|
67
94
|
}
|
|
@@ -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(
|
|
10
|
-
$(document).
|
|
11
|
-
if (e.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
105
|
+
span.append(this.unitLabelHTML());
|
|
106
|
+
span.append(document.createTextNode(" " + formattedNumber));
|
|
85
107
|
}
|
|
86
108
|
else {
|
|
87
|
-
|
|
109
|
+
span.append(document.createTextNode(formattedNumber + " "));
|
|
110
|
+
span.append(this.unitLabelHTML());
|
|
88
111
|
}
|
|
89
112
|
}
|
|
90
113
|
else if (this.isNil()) {
|
|
91
|
-
|
|
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
|
-
|
|
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
|
|
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
|
});
|