ixbrl-viewer 1.4.20__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.
- iXBRLViewerPlugin/__init__.py +77 -49
- iXBRLViewerPlugin/_version.py +2 -2
- iXBRLViewerPlugin/constants.py +86 -1
- iXBRLViewerPlugin/featureConfig.py +4 -1
- iXBRLViewerPlugin/iXBRLViewer.py +202 -131
- 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 +27 -2
- 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 +41 -8
- iXBRLViewerPlugin/viewer/src/js/fact.test.js +47 -13
- 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 +22 -0
- iXBRLViewerPlugin/viewer/src/js/summary.test.js +50 -13
- 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.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/METADATA +41 -14
- ixbrl_viewer-1.4.49.dist-info/RECORD +197 -0
- {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/WHEEL +1 -1
- tests/puppeteer/framework/page_objects/doc_frame.js +3 -3
- tests/puppeteer/framework/page_objects/fact_details_panel.js +28 -0
- 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.20.dist-info/RECORD +0 -166
- {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/LICENSE +0 -0
- {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/NOTICE +0 -0
- {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/entry_points.txt +0 -0
- {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/top_level.txt +0 -0
|
@@ -17,16 +17,17 @@ function testFact(conceptName, dimensions) {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function testReportSet(concepts, facts, documents) {
|
|
20
|
+
function testReportSet(concepts, facts, documents, softwareCredits) {
|
|
21
21
|
const report = {
|
|
22
22
|
getConcept: conceptName => concepts[conceptName],
|
|
23
|
-
localDocuments: () => documents
|
|
23
|
+
localDocuments: () => documents,
|
|
24
24
|
}
|
|
25
25
|
for (const f of facts) {
|
|
26
26
|
f.report = report;
|
|
27
27
|
}
|
|
28
28
|
return {
|
|
29
29
|
facts: () => facts,
|
|
30
|
+
getSoftwareCredits: () => softwareCredits,
|
|
30
31
|
reports: [ report ]
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -34,7 +35,7 @@ function testReportSet(concepts, facts, documents) {
|
|
|
34
35
|
describe("Facts summary", () => {
|
|
35
36
|
|
|
36
37
|
test("no facts", () => {
|
|
37
|
-
const reportSet = testReportSet({}, [], {});
|
|
38
|
+
const reportSet = testReportSet({}, [], {}, []);
|
|
38
39
|
const summary = new DocumentSummary(reportSet);
|
|
39
40
|
|
|
40
41
|
expect(summary.totalFacts()).toBe(0);
|
|
@@ -43,9 +44,9 @@ describe("Facts summary", () => {
|
|
|
43
44
|
test("multiple facts", () => {
|
|
44
45
|
const facts = [];
|
|
45
46
|
for (let i = 0; i < 10; i++) {
|
|
46
|
-
facts.push(testFact("eg:Concept1", {}));
|
|
47
|
+
facts.push(testFact("eg:Concept1", {}, []));
|
|
47
48
|
}
|
|
48
|
-
const reportSet = testReportSet({}, facts, {});
|
|
49
|
+
const reportSet = testReportSet({}, facts, {}, []);
|
|
49
50
|
const summary = new DocumentSummary(reportSet);
|
|
50
51
|
|
|
51
52
|
expect(summary.totalFacts()).toBe(10);
|
|
@@ -55,7 +56,7 @@ describe("Facts summary", () => {
|
|
|
55
56
|
const conceptName = "eg:Concept1";
|
|
56
57
|
const fact1 = testFact(conceptName, {});
|
|
57
58
|
const fact2 = testFact(conceptName, {});
|
|
58
|
-
const reportSet = testReportSet({}, [fact1, fact2], {});
|
|
59
|
+
const reportSet = testReportSet({}, [fact1, fact2], {}, []);
|
|
59
60
|
const summary = new DocumentSummary(reportSet);
|
|
60
61
|
|
|
61
62
|
expect(summary.totalFacts()).toBe(2);
|
|
@@ -66,7 +67,7 @@ describe("Facts summary", () => {
|
|
|
66
67
|
describe("Tags summary", () => {
|
|
67
68
|
|
|
68
69
|
test("no tags", () => {
|
|
69
|
-
const reportSet = testReportSet({}, [], {})
|
|
70
|
+
const reportSet = testReportSet({}, [], {}, [])
|
|
70
71
|
const summary = new DocumentSummary(reportSet);
|
|
71
72
|
|
|
72
73
|
expect(summary.tagCounts()).toEqual(new Map());
|
|
@@ -76,7 +77,7 @@ describe("Tags summary", () => {
|
|
|
76
77
|
const fact1 = testFact("eg:Concept1", {});
|
|
77
78
|
const fact2 = testFact("eg:Concept2", {});
|
|
78
79
|
const fact3 = testFact("xz:Concept1", {});
|
|
79
|
-
const reportSet = testReportSet({}, [fact1, fact2, fact3], {})
|
|
80
|
+
const reportSet = testReportSet({}, [fact1, fact2, fact3], {}, [])
|
|
80
81
|
const summary = new DocumentSummary(reportSet);
|
|
81
82
|
|
|
82
83
|
expect(Object.fromEntries(summary.tagCounts())).toEqual({
|
|
@@ -105,7 +106,7 @@ describe("Tags summary", () => {
|
|
|
105
106
|
const concepts = {
|
|
106
107
|
[dimension]: testConcept(),
|
|
107
108
|
}
|
|
108
|
-
const reportSet = testReportSet(concepts, [fact], {})
|
|
109
|
+
const reportSet = testReportSet(concepts, [fact], {}, [])
|
|
109
110
|
const summary = new DocumentSummary(reportSet);
|
|
110
111
|
|
|
111
112
|
expect(Object.fromEntries(summary.tagCounts())).toEqual({
|
|
@@ -135,7 +136,7 @@ describe("Tags summary", () => {
|
|
|
135
136
|
[dimension2]: testConcept(),
|
|
136
137
|
[dimension3]: testConcept(),
|
|
137
138
|
}
|
|
138
|
-
const reportSet = testReportSet(concepts, [fact], {})
|
|
139
|
+
const reportSet = testReportSet(concepts, [fact], {}, [])
|
|
139
140
|
const summary = new DocumentSummary(reportSet);
|
|
140
141
|
|
|
141
142
|
expect(Object.fromEntries(summary.tagCounts())).toEqual({
|
|
@@ -176,7 +177,7 @@ describe("Tags summary", () => {
|
|
|
176
177
|
const concepts = {
|
|
177
178
|
[dimension]: testConcept(typedDomain),
|
|
178
179
|
}
|
|
179
|
-
const reportSet = testReportSet(concepts, [fact], {})
|
|
180
|
+
const reportSet = testReportSet(concepts, [fact], {}, [])
|
|
180
181
|
const summary = new DocumentSummary(reportSet);
|
|
181
182
|
|
|
182
183
|
expect(Object.fromEntries(summary.tagCounts())).toEqual({
|
|
@@ -200,7 +201,7 @@ describe("Files summary", () => {
|
|
|
200
201
|
|
|
201
202
|
test("no files", () => {
|
|
202
203
|
const documentData = {}
|
|
203
|
-
const reportSet = testReportSet({}, [], documentData);
|
|
204
|
+
const reportSet = testReportSet({}, [], documentData, []);
|
|
204
205
|
const summary = new DocumentSummary(reportSet);
|
|
205
206
|
|
|
206
207
|
expect(summary.getLocalDocuments()).toEqual({
|
|
@@ -228,7 +229,7 @@ describe("Files summary", () => {
|
|
|
228
229
|
'labelLinkbase.xml': ['labelLinkbase'],
|
|
229
230
|
'unrecognizedLinkbase.xml': ['unrecognizedLinkbase'],
|
|
230
231
|
}
|
|
231
|
-
const reportSet = testReportSet({}, [], documentData);
|
|
232
|
+
const reportSet = testReportSet({}, [], documentData, []);
|
|
232
233
|
const summary = new DocumentSummary(reportSet);
|
|
233
234
|
|
|
234
235
|
expect(summary.getLocalDocuments()).toEqual({
|
|
@@ -243,3 +244,39 @@ describe("Files summary", () => {
|
|
|
243
244
|
});
|
|
244
245
|
});
|
|
245
246
|
});
|
|
247
|
+
|
|
248
|
+
describe("Software credit", () => {
|
|
249
|
+
|
|
250
|
+
test("no credits", () => {
|
|
251
|
+
const softwareCredits = []
|
|
252
|
+
const documentData = {}
|
|
253
|
+
const reportSet = testReportSet({}, [], documentData, softwareCredits);
|
|
254
|
+
const summary = new DocumentSummary(reportSet);
|
|
255
|
+
|
|
256
|
+
expect(summary.getSoftwareCredits()).toEqual([]);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("one credit", () => {
|
|
260
|
+
const softwareCredits = ["Example credit text"]
|
|
261
|
+
const documentData = {}
|
|
262
|
+
const reportSet = testReportSet({}, [], documentData, softwareCredits);
|
|
263
|
+
const summary = new DocumentSummary(reportSet);
|
|
264
|
+
|
|
265
|
+
expect(summary.getSoftwareCredits()).toEqual(["Example credit text"]);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("multiple credits", () => {
|
|
269
|
+
const softwareCredits = [
|
|
270
|
+
"Example credit text A",
|
|
271
|
+
"Example credit text B",
|
|
272
|
+
]
|
|
273
|
+
const documentData = {}
|
|
274
|
+
const reportSet = testReportSet({}, [], documentData, softwareCredits);
|
|
275
|
+
const summary = new DocumentSummary(reportSet);
|
|
276
|
+
|
|
277
|
+
expect(summary.getSoftwareCredits()).toEqual([
|
|
278
|
+
"Example credit text A",
|
|
279
|
+
"Example credit text B",
|
|
280
|
+
]);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// See COPYRIGHT.md for copyright information
|
|
2
2
|
|
|
3
3
|
import $ from 'jquery'
|
|
4
|
-
import FileSaver from 'file-saver'
|
|
5
4
|
import writeXlsxFile from 'write-excel-file'
|
|
6
5
|
import { Fact } from './fact.js';
|
|
7
6
|
|
|
@@ -15,12 +14,13 @@ export class TableExport {
|
|
|
15
14
|
static addHandles(iframe, reportSet) {
|
|
16
15
|
$('table', iframe).each(function () {
|
|
17
16
|
const table = $(this);
|
|
18
|
-
|
|
17
|
+
// Require at least two facts on different rows.
|
|
18
|
+
if (table.find("tr").filter((i, row) => $(row).find(".ixbrl-element").length > 0).length > 1) {
|
|
19
19
|
table.css("position", "relative");
|
|
20
20
|
const exporter = new TableExport(table, reportSet);
|
|
21
21
|
$('<div class="ixbrl-table-handle"><span>Export table</span></div>')
|
|
22
22
|
.appendTo(table)
|
|
23
|
-
.click(
|
|
23
|
+
.on("click", () => exporter.exportTable());
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// See COPYRIGHT.md for copyright information
|
|
2
|
+
|
|
3
|
+
export class TaxonomyNamer {
|
|
4
|
+
constructor(map) {
|
|
5
|
+
this.map = new Map(Array.from(map.entries()).map(([k,v]) => [new RegExp(k), new TaxonomyName(v[0], v[1])]));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
getName(prefix, uri) {
|
|
10
|
+
for (const [re, name] of this.map.entries()) {
|
|
11
|
+
const m = uri.match(re);
|
|
12
|
+
if (m !== null) {
|
|
13
|
+
return name;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return new TaxonomyName(prefix, prefix);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fromQName(qname) {
|
|
20
|
+
return this.getName(qname.prefix, qname.namespace);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
convertQName(qname) {
|
|
24
|
+
const name = this.fromQName(qname)
|
|
25
|
+
return name.prefix + ':' + qname.localname;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class TaxonomyName {
|
|
30
|
+
constructor(prefix, name) {
|
|
31
|
+
this.prefix = prefix;
|
|
32
|
+
this.name = name;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// See COPYRIGHT.md for copyright information
|
|
2
|
+
|
|
3
|
+
import { TaxonomyNamer } from './taxonomynamer.js';
|
|
4
|
+
import { QName } from './qname.js';
|
|
5
|
+
|
|
6
|
+
const prefixMap = {
|
|
7
|
+
"e": "http://example.com/",
|
|
8
|
+
"g": "http://eggsample.com/",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const preferredPrefixMap = new Map([
|
|
12
|
+
[ "http://ex.*\.com/", ["prefix1", "My Example"] ]
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
function qname(s) {
|
|
16
|
+
return new QName(prefixMap, s);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("readableName", () => {
|
|
20
|
+
const namer = new TaxonomyNamer(preferredPrefixMap);
|
|
21
|
+
test("Taxonomy name match", () => {
|
|
22
|
+
expect(namer.fromQName(qname("e:1234")).prefix).toBe("prefix1");
|
|
23
|
+
expect(namer.fromQName(qname("e:1234")).name).toBe("My Example");
|
|
24
|
+
expect(namer.convertQName(qname("e:1234"))).toBe("prefix1:1234");
|
|
25
|
+
});
|
|
26
|
+
test("Unknown taxonomy", () => {
|
|
27
|
+
expect(namer.fromQName(qname("g:1234")).prefix).toBe("g");
|
|
28
|
+
expect(namer.fromQName(qname("g:1234")).name).toBe("g");
|
|
29
|
+
expect(namer.convertQName(qname("g:1234"))).toBe("g:1234");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// See COPYRIGHT.md for copyright information
|
|
2
|
+
|
|
3
|
+
import { STORAGE_THEME } from "./util";
|
|
4
|
+
|
|
5
|
+
const DARK_THEME = 'dark';
|
|
6
|
+
const LIGHT_THEME = 'light';
|
|
7
|
+
|
|
8
|
+
function getHtmlElement() {
|
|
9
|
+
return document.querySelector('html');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getVariable(name) {
|
|
13
|
+
const html = getHtmlElement();
|
|
14
|
+
return getComputedStyle(html).getPropertyValue(name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function setTheme(theme) {
|
|
18
|
+
const html = getHtmlElement();
|
|
19
|
+
html.dataset.theme = `theme-${theme}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getStoredTheme() {
|
|
23
|
+
return localStorage.getItem(STORAGE_THEME);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function storeTheme(theme) {
|
|
27
|
+
localStorage.setItem(STORAGE_THEME, theme);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getTheme() {
|
|
31
|
+
const html = getHtmlElement();
|
|
32
|
+
return html.dataset.theme.replace('theme-','');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function initializeTheme() {
|
|
36
|
+
const storedTheme = getStoredTheme();
|
|
37
|
+
if (storedTheme !== null) {
|
|
38
|
+
setTheme(storedTheme);
|
|
39
|
+
} else {
|
|
40
|
+
setTheme(window.matchMedia(`(prefers-color-scheme: ${DARK_THEME})`).matches ? DARK_THEME : LIGHT_THEME);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function toggleTheme() {
|
|
45
|
+
const currentTheme = getTheme();
|
|
46
|
+
const newTheme = currentTheme === LIGHT_THEME ? DARK_THEME : LIGHT_THEME;
|
|
47
|
+
setTheme(newTheme);
|
|
48
|
+
storeTheme(newTheme);
|
|
49
|
+
}
|
|
@@ -1,9 +1,42 @@
|
|
|
1
1
|
// See COPYRIGHT.md for copyright information
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { NAMESPACE_ISO4217 } from "./util";
|
|
4
|
+
import i18next from "i18next";
|
|
5
|
+
import { utr } from "./utr";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Transforms measure qname into title case label (or currency symbol, if applicable).
|
|
9
|
+
* @return {String} Measure Label
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
function measureLabel(report, measure) {
|
|
13
|
+
const qname = report.qname(measure);
|
|
14
|
+
if (qname.namespace === NAMESPACE_ISO4217) {
|
|
15
|
+
// Prefer a name from our own i18n resources
|
|
16
|
+
const keyi18n = `currencies:unitFormat${qname.localname}`;
|
|
17
|
+
if (i18next.exists(keyi18n)) {
|
|
18
|
+
return i18next.t(keyi18n);
|
|
19
|
+
}
|
|
20
|
+
// Fall back on symbol from UTR ...
|
|
21
|
+
const utrEntry = utr.get(qname);
|
|
22
|
+
if (utrEntry !== undefined) {
|
|
23
|
+
// ... but disambiguate "$" symbol
|
|
24
|
+
return utrEntry.symbol == '$' ? `${qname.localname} $` : utrEntry.symbol;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (measure.includes(':')) {
|
|
28
|
+
return measure.split(':')[1];
|
|
29
|
+
}
|
|
30
|
+
return measure;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function measureName(report, measure) {
|
|
34
|
+
const qname = report.qname(measure);
|
|
35
|
+
const utrEntry = utr.get(qname);
|
|
36
|
+
return utrEntry?.name;
|
|
37
|
+
}
|
|
4
38
|
|
|
5
39
|
export class Unit {
|
|
6
|
-
|
|
7
40
|
constructor(reportSet, unitKey) {
|
|
8
41
|
this._reportSet = reportSet;
|
|
9
42
|
this._value = unitKey;
|
|
@@ -50,5 +83,43 @@ export class Unit {
|
|
|
50
83
|
value() {
|
|
51
84
|
return this._value;
|
|
52
85
|
}
|
|
86
|
+
|
|
87
|
+
measureHTML(m) {
|
|
88
|
+
const span = document.createElement("span");
|
|
89
|
+
const name = measureName(this._reportSet, m);
|
|
90
|
+
if (name !== undefined) {
|
|
91
|
+
span.setAttribute("title", name);
|
|
92
|
+
span.classList.add("measure");
|
|
93
|
+
}
|
|
94
|
+
span.append(document.createTextNode(measureLabel(this._reportSet, m)));
|
|
95
|
+
return span;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
partsHTML(parts) {
|
|
99
|
+
const span = document.createElement("span");
|
|
100
|
+
if (parts.length > 1) {
|
|
101
|
+
span.appendChild(document.createTextNode("("));
|
|
102
|
+
}
|
|
103
|
+
for (const [i, m] of parts.entries()) {
|
|
104
|
+
span.appendChild(this.measureHTML(m));
|
|
105
|
+
if (i < parts.length - 1) {
|
|
106
|
+
span.appendChild(document.createTextNode(" * "))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (parts.length > 1) {
|
|
110
|
+
span.appendChild(document.createTextNode(")"));
|
|
111
|
+
}
|
|
112
|
+
return span;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
html() {
|
|
116
|
+
const span = document.createElement("span");
|
|
117
|
+
span.append(this.partsHTML(this._numerators));
|
|
118
|
+
if (this._denominators.length > 0) {
|
|
119
|
+
span.append(document.createTextNode(" / "));
|
|
120
|
+
span.append(this.partsHTML(this._denominators));
|
|
121
|
+
}
|
|
122
|
+
return span;
|
|
123
|
+
}
|
|
53
124
|
}
|
|
54
125
|
|
|
@@ -11,7 +11,7 @@ var testReportData = {
|
|
|
11
11
|
"iso4217": NAMESPACE_ISO4217,
|
|
12
12
|
"e": "http://example.com/entity",
|
|
13
13
|
},
|
|
14
|
-
"facts": {}
|
|
14
|
+
"facts": {},
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
function testReport() {
|
|
@@ -57,12 +57,23 @@ describe("Unit label", () => {
|
|
|
57
57
|
|
|
58
58
|
test("Unit label - known currency", () => {
|
|
59
59
|
var unit = new Unit(testReport(), 'iso4217:USD');
|
|
60
|
+
// Note "US $" from i18n takes precedence over "USD $" generated from UTR
|
|
60
61
|
expect(unit.label()).toEqual('US $');
|
|
61
62
|
});
|
|
62
63
|
|
|
64
|
+
test("Unit label - UTR currency", () => {
|
|
65
|
+
var unit = new Unit(testReport(), 'iso4217:THB');
|
|
66
|
+
expect(unit.label()).toEqual('฿');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("Unit label - UTR currency with '$' symbol", () => {
|
|
70
|
+
var unit = new Unit(testReport(), 'iso4217:SGD');
|
|
71
|
+
expect(unit.label()).toEqual('SGD $');
|
|
72
|
+
});
|
|
73
|
+
|
|
63
74
|
test("Unit label - unknown", () => {
|
|
64
|
-
var unit = new Unit(testReport(), 'iso4217:
|
|
65
|
-
expect(unit.label()).toEqual('
|
|
75
|
+
var unit = new Unit(testReport(), 'iso4217:ZZZZ');
|
|
76
|
+
expect(unit.label()).toEqual('ZZZZ');
|
|
66
77
|
});
|
|
67
78
|
});
|
|
68
79
|
|
|
@@ -2,16 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
import moment from "moment";
|
|
4
4
|
import Decimal from "decimal.js";
|
|
5
|
-
import i18next from "i18next";
|
|
6
|
-
|
|
7
5
|
|
|
8
6
|
export const SHOW_FACT = 'SHOW_FACT';
|
|
9
7
|
|
|
10
8
|
export const NAMESPACE_ISO4217 = 'http://www.xbrl.org/2003/iso4217';
|
|
9
|
+
export const NAMESPACE_XBRLI = 'http://www.xbrl.org/2003/instance';
|
|
10
|
+
|
|
11
|
+
export const CALC_ARCROLE = "calc";
|
|
12
|
+
export const CALC11_ARCROLE = "calc11";
|
|
13
|
+
|
|
14
|
+
export const GLOSSARY_URL = "https://xbrl.org/glossary/";
|
|
11
15
|
|
|
12
16
|
// The number of distinct highlight colors defined in inspector.less
|
|
13
17
|
export const HIGHLIGHT_COLORS = 3;
|
|
14
18
|
|
|
19
|
+
// Feature names
|
|
20
|
+
export const FEATURE_GUIDE_LINK = 'guide_link';
|
|
21
|
+
export const FEATURE_HOME_LINK_LABEL = 'home_link_label';
|
|
22
|
+
export const FEATURE_HOME_LINK_URL = 'home_link_url';
|
|
23
|
+
export const FEATURE_REVIEW = 'review';
|
|
24
|
+
export const FEATURE_SUPPORT_LINK = 'support_link';
|
|
25
|
+
export const FEATURE_SURVEY_LINK = 'survey_link';
|
|
26
|
+
export const FEATURE_SEARCH_ON_STARTUP = 'search_on_startup';
|
|
27
|
+
export const FEATURE_HIGHLIGHT_FACTS_ON_STARTUP = 'highlight_facts_on_startup';
|
|
28
|
+
|
|
29
|
+
export const STORAGE_THEME = "ixbrl-viewer-theme";
|
|
30
|
+
export const STORAGE_HIGHLIGHT_FACTS = "ixbrl-viewer-highlight-all-facts";
|
|
31
|
+
export const STORAGE_HOME_LINK_QUERY = "ixbrl-viewer-home-link-query";
|
|
32
|
+
|
|
15
33
|
/*
|
|
16
34
|
* Takes a moment.js oject and converts it to a human readable date, or date
|
|
17
35
|
* and time if the time component is not midnight. Adjust specifies that a
|
|
@@ -205,7 +223,7 @@ export function localId(viewerUniqueId) {
|
|
|
205
223
|
|
|
206
224
|
export function getIXHiddenLinkStyle(domNode) {
|
|
207
225
|
if (domNode.hasAttribute('style')) {
|
|
208
|
-
const re = /(?:^|\s|;)
|
|
226
|
+
const re = /(?:^|\s|;)(?:-sec|-esef)?-ix-hidden:\s*([^\s;]+)/;
|
|
209
227
|
const m = domNode.getAttribute('style').match(re);
|
|
210
228
|
if (m) {
|
|
211
229
|
return m[1];
|
|
@@ -213,18 +231,3 @@ export function getIXHiddenLinkStyle(domNode) {
|
|
|
213
231
|
}
|
|
214
232
|
return null;
|
|
215
233
|
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Transforms measure qname into title case label (or currency symbol, if applicable).
|
|
219
|
-
* @return {String} Measure Label
|
|
220
|
-
*/
|
|
221
|
-
|
|
222
|
-
export function measureLabel(report, measure) {
|
|
223
|
-
const qname = report.qname(measure);
|
|
224
|
-
if (qname.namespace === NAMESPACE_ISO4217) {
|
|
225
|
-
measure = i18next.t(`currencies:unitFormat${qname.localname}`, {defaultValue: qname.localname});
|
|
226
|
-
} else if (measure.includes(':')) {
|
|
227
|
-
measure = measure.split(':')[1];
|
|
228
|
-
}
|
|
229
|
-
return measure;
|
|
230
|
-
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// See COPYRIGHT.md for copyright information
|
|
2
|
+
|
|
3
|
+
// Class to expose properties of Unit Type Registry entries from viewer data
|
|
4
|
+
|
|
5
|
+
export class UTREntry {
|
|
6
|
+
|
|
7
|
+
constructor(symbol, name) {
|
|
8
|
+
this.symbol = symbol;
|
|
9
|
+
this.name = name;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class UTR {
|
|
14
|
+
constructor() {
|
|
15
|
+
self.entries = require("../data/utr.json");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get(qname) {
|
|
19
|
+
const u = self.entries[qname.namespace]?.[qname.localname]
|
|
20
|
+
if (u === undefined) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return new UTREntry(u.s ?? u.n, u.n)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const utr = new UTR();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// See COPYRIGHT.md for copyright information
|
|
2
2
|
|
|
3
3
|
import $ from 'jquery'
|
|
4
|
-
import { numberMatchSearch
|
|
4
|
+
import { numberMatchSearch } from './number-matcher.js'
|
|
5
5
|
import { TableExport } from './tableExport.js'
|
|
6
6
|
import { IXNode } from './ixnode.js';
|
|
7
|
-
import { getIXHiddenLinkStyle, runGenerator,
|
|
7
|
+
import { getIXHiddenLinkStyle, runGenerator, viewerUniqueId, HIGHLIGHT_COLORS } from './util.js';
|
|
8
8
|
import { DocOrderIndex } from './docOrderIndex.js';
|
|
9
9
|
import { MessageBox } from './messagebox.js';
|
|
10
10
|
|
|
@@ -65,7 +65,7 @@ export class Viewer {
|
|
|
65
65
|
viewer._iframes.each(function (docIndex) {
|
|
66
66
|
$(this).data("selected", docIndex == viewer._currentDocumentIndex);
|
|
67
67
|
const reportIndex = $(this).data("report-index");
|
|
68
|
-
viewer._preProcessiXBRL($(this).contents().find("body").get(0), reportIndex, docIndex);
|
|
68
|
+
viewer._preProcessiXBRL($(this).contents().find("body").get(0), reportIndex, docIndex, false);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
/* Call plugin promise for each document in turn */
|
|
@@ -108,11 +108,11 @@ export class Viewer {
|
|
|
108
108
|
if (this._reportSet.isMultiDocumentViewer()) {
|
|
109
109
|
$('#ixv .ixds-tabs').show();
|
|
110
110
|
for (const [i, doc] of this._reportSet.reportFiles().entries()) {
|
|
111
|
-
$('<
|
|
111
|
+
$('<button class="tab"></button>')
|
|
112
112
|
.text(doc.file)
|
|
113
113
|
.prop('title', doc.file)
|
|
114
114
|
.data('ix-doc-id', i)
|
|
115
|
-
.click(
|
|
115
|
+
.on("click", () => this.selectDocument(i))
|
|
116
116
|
.appendTo($('#ixv #viewer-pane .ixds-tabs .tab-area'));
|
|
117
117
|
}
|
|
118
118
|
$('#ixv #viewer-pane .ixds-tabs .tab-area .tab').eq(0).addClass("active");
|
|
@@ -123,12 +123,17 @@ export class Viewer {
|
|
|
123
123
|
// display: block, a div is used, otherwise a span. Returns the wrapper node
|
|
124
124
|
// as a jQuery node
|
|
125
125
|
_wrapNode(n) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
let wrapper = "<span>";
|
|
127
|
+
if (getComputedStyle(n).getPropertyValue("display") === "block") {
|
|
128
|
+
wrapper = '<div>';
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const nn = n.getElementsByTagName("*");
|
|
132
|
+
for (var i = 0; i < nn.length; i++) {
|
|
133
|
+
if (getComputedStyle(nn[i]).getPropertyValue('display') === "block") {
|
|
134
|
+
wrapper = '<div>';
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
132
137
|
}
|
|
133
138
|
}
|
|
134
139
|
$(n).wrap(wrapper);
|
|
@@ -232,7 +237,7 @@ export class Viewer {
|
|
|
232
237
|
const [file, fragment] = url.split('#', 2);
|
|
233
238
|
const docIndex = this._reportSet.reportFiles().indexOf(file);
|
|
234
239
|
if (!url.includes('/') && docIndex != -1) {
|
|
235
|
-
$(n).click(
|
|
240
|
+
$(n).on("click", (e) => {
|
|
236
241
|
this._showDocumentAndElement(docIndex, fragment);
|
|
237
242
|
e.preventDefault();
|
|
238
243
|
});
|
|
@@ -268,7 +273,7 @@ export class Viewer {
|
|
|
268
273
|
nodes.push(tableNode)
|
|
269
274
|
}
|
|
270
275
|
}
|
|
271
|
-
/* Otherwise, insert a <span> as wrapper */
|
|
276
|
+
/* Otherwise, insert a <span> or <div> as wrapper */
|
|
272
277
|
if (nodes.length == 0) {
|
|
273
278
|
nodes.push(this._wrapNode(domNode));
|
|
274
279
|
}
|
|
@@ -440,7 +445,7 @@ export class Viewer {
|
|
|
440
445
|
// Handle SEC/ESEF links-to-hidden
|
|
441
446
|
const vuid = viewerUniqueId(reportIndex, getIXHiddenLinkStyle(n));
|
|
442
447
|
if (vuid !== null) {
|
|
443
|
-
let nodes =
|
|
448
|
+
let nodes = this._findOrCreateWrapperNode(n, inHidden);
|
|
444
449
|
nodes.addClass("ixbrl-element").data('ivids', [vuid]);
|
|
445
450
|
this._docOrderItemIndex.addItem(vuid, docIndex);
|
|
446
451
|
/* We may have already seen the corresponding ix element in the hidden
|
|
@@ -472,7 +477,7 @@ export class Viewer {
|
|
|
472
477
|
_applyStyles() {
|
|
473
478
|
const stlyeElts = $("<style>")
|
|
474
479
|
.prop("type", "text/css")
|
|
475
|
-
.text(require('
|
|
480
|
+
.text(require('../less/viewer.less').toString())
|
|
476
481
|
.appendTo(this._iframes.contents().find("head"));
|
|
477
482
|
this._iv.callPluginMethod("updateViewerStyleElements", stlyeElts);
|
|
478
483
|
}
|
|
@@ -512,18 +517,18 @@ export class Viewer {
|
|
|
512
517
|
_bindHandlers() {
|
|
513
518
|
const viewer = this;
|
|
514
519
|
$('.ixbrl-element', this._contents)
|
|
515
|
-
.click
|
|
520
|
+
.on("click", function (e) {
|
|
516
521
|
e.stopPropagation();
|
|
517
522
|
viewer.selectElementByClick($(this));
|
|
518
523
|
})
|
|
519
|
-
.mouseenter
|
|
520
|
-
.mouseleave
|
|
524
|
+
.on("mouseenter", function (e) { viewer._mouseEnter($(this)) })
|
|
525
|
+
.on("mouseleave", function (e) { viewer._mouseLeave($(this)) });
|
|
521
526
|
$("body", this._contents)
|
|
522
|
-
.click(
|
|
527
|
+
.on("click", () => viewer.selectElement(null));
|
|
523
528
|
|
|
524
|
-
$('#iframe-container .zoom-in').click(
|
|
525
|
-
$('#iframe-container .zoom-out').click(
|
|
526
|
-
$('#iframe-container .print').click(
|
|
529
|
+
$('#iframe-container .zoom-in').on("click", () => this.zoomIn());
|
|
530
|
+
$('#iframe-container .zoom-out').on("click", () => this.zoomOut());
|
|
531
|
+
$('#iframe-container .print').on("click", () => this.currentDocument().get(0).contentWindow.print());
|
|
527
532
|
|
|
528
533
|
TableExport.addHandles(this._contents, this._reportSet);
|
|
529
534
|
}
|
|
@@ -593,7 +598,7 @@ export class Viewer {
|
|
|
593
598
|
/* If the specified element is not fully visible, scroll it into the center of
|
|
594
599
|
* the viewport */
|
|
595
600
|
showElement(e) {
|
|
596
|
-
const ee = e.get(0);
|
|
601
|
+
const ee = e.filter(':not(.ixbrl-no-highlight)').get(0);
|
|
597
602
|
if (!this.isFullyVisible(ee)) {
|
|
598
603
|
ee.scrollIntoView({ block: "center", inline: "center" });
|
|
599
604
|
}
|
|
@@ -749,11 +754,14 @@ export class Viewer {
|
|
|
749
754
|
// highlight color for an element that is double tagged in a
|
|
750
755
|
// table cell.
|
|
751
756
|
const ixn = $(this).data('ivids').map(id => viewer._ixNodeMap[id]).filter(ixn => !ixn.footnote)[0];
|
|
752
|
-
if (ixn
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
+
if (ixn !== undefined ) {
|
|
758
|
+
const item = reportSet.getItemById(ixn.id);
|
|
759
|
+
if (item !== undefined) {
|
|
760
|
+
const elements = viewer.elementsForItemIds(ixn.chainIXIds());
|
|
761
|
+
const i = groups[item.conceptQName().prefix];
|
|
762
|
+
if (i !== undefined) {
|
|
763
|
+
elements.addClass("ixbrl-highlight-" + i);
|
|
764
|
+
}
|
|
757
765
|
}
|
|
758
766
|
}
|
|
759
767
|
});
|
|
@@ -803,7 +811,10 @@ export class Viewer {
|
|
|
803
811
|
}
|
|
804
812
|
|
|
805
813
|
_setTitle(docIndex) {
|
|
806
|
-
|
|
814
|
+
const title = $('head title', this._iframes.eq(docIndex).contents()).text();
|
|
815
|
+
$('#top-bar .document-title')
|
|
816
|
+
.text(title)
|
|
817
|
+
.attr("aria-label", "Inline Viewer: " + title);
|
|
807
818
|
}
|
|
808
819
|
|
|
809
820
|
showDocumentForItemId(vuid) {
|