ixbrl-viewer 1.4.21__py3-none-any.whl → 1.4.50__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (114) hide show
  1. iXBRLViewerPlugin/__init__.py +77 -49
  2. iXBRLViewerPlugin/_version.py +2 -2
  3. iXBRLViewerPlugin/constants.py +86 -1
  4. iXBRLViewerPlugin/featureConfig.py +4 -1
  5. iXBRLViewerPlugin/iXBRLViewer.py +210 -137
  6. iXBRLViewerPlugin/plugin.py +7 -0
  7. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
  8. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt +9 -2
  9. iXBRLViewerPlugin/viewer/i18next-parser.config.js +1 -1
  10. iXBRLViewerPlugin/viewer/src/data/utr.json +1 -0
  11. iXBRLViewerPlugin/viewer/src/html/fact-details.html +69 -38
  12. iXBRLViewerPlugin/viewer/src/html/footer-logo.html +4 -0
  13. iXBRLViewerPlugin/viewer/src/html/footnote-details.html +1 -1
  14. iXBRLViewerPlugin/viewer/src/html/inspector.html +324 -211
  15. iXBRLViewerPlugin/viewer/src/i18n/cy/balancetypes.json +1 -0
  16. iXBRLViewerPlugin/viewer/src/i18n/cy/currencies.json +13 -0
  17. iXBRLViewerPlugin/viewer/src/i18n/cy/datatypes.json +9 -0
  18. iXBRLViewerPlugin/viewer/src/i18n/cy/labelroles.json +24 -0
  19. iXBRLViewerPlugin/viewer/src/i18n/cy/referenceparts.json +10 -0
  20. iXBRLViewerPlugin/viewer/src/i18n/cy/scale.json +16 -0
  21. iXBRLViewerPlugin/viewer/src/i18n/cy/tooltips.json +17 -0
  22. iXBRLViewerPlugin/viewer/src/i18n/cy/translation.json +179 -0
  23. iXBRLViewerPlugin/viewer/src/i18n/en/balancetypes.json +4 -0
  24. iXBRLViewerPlugin/viewer/src/i18n/en/datatypes.json +10 -0
  25. iXBRLViewerPlugin/viewer/src/i18n/en/labelroles.json +4 -0
  26. iXBRLViewerPlugin/viewer/src/i18n/en/scale.json +16 -0
  27. iXBRLViewerPlugin/viewer/src/i18n/en/tooltips.json +17 -0
  28. iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +56 -24
  29. iXBRLViewerPlugin/viewer/src/i18n/es/balancetypes.json +4 -0
  30. iXBRLViewerPlugin/viewer/src/i18n/es/datatypes.json +10 -0
  31. iXBRLViewerPlugin/viewer/src/i18n/es/labelroles.json +24 -0
  32. iXBRLViewerPlugin/viewer/src/i18n/es/scale.json +16 -0
  33. iXBRLViewerPlugin/viewer/src/i18n/es/tooltips.json +17 -0
  34. iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +70 -37
  35. iXBRLViewerPlugin/viewer/src/icons/dark-mode.svg +4 -0
  36. iXBRLViewerPlugin/viewer/src/img/arelle-dark.svg +179 -0
  37. iXBRLViewerPlugin/viewer/src/img/inline-viewer-dark.svg +59 -0
  38. iXBRLViewerPlugin/viewer/src/js/accordian.js +3 -2
  39. iXBRLViewerPlugin/viewer/src/js/aspect.js +18 -10
  40. iXBRLViewerPlugin/viewer/src/js/aspect.test.js +2 -2
  41. iXBRLViewerPlugin/viewer/src/js/balance.js +14 -0
  42. iXBRLViewerPlugin/viewer/src/js/calculation.js +45 -34
  43. iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +4 -7
  44. iXBRLViewerPlugin/viewer/src/js/chart.js +23 -21
  45. iXBRLViewerPlugin/viewer/src/js/concept.js +30 -3
  46. iXBRLViewerPlugin/viewer/src/js/concept.test.js +23 -2
  47. iXBRLViewerPlugin/viewer/src/js/datatype.js +20 -0
  48. iXBRLViewerPlugin/viewer/src/js/datatype.test.js +62 -0
  49. iXBRLViewerPlugin/viewer/src/js/dialog.js +6 -4
  50. iXBRLViewerPlugin/viewer/src/js/fact.js +40 -7
  51. iXBRLViewerPlugin/viewer/src/js/fact.test.js +3 -0
  52. iXBRLViewerPlugin/viewer/src/js/index.js +11 -3
  53. iXBRLViewerPlugin/viewer/src/js/inspector.js +560 -160
  54. iXBRLViewerPlugin/viewer/src/js/inspector.test.js +1 -2
  55. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.js +129 -31
  56. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.test.js +133 -20
  57. iXBRLViewerPlugin/viewer/src/js/ixnode.js +1 -1
  58. iXBRLViewerPlugin/viewer/src/js/menu.js +25 -7
  59. iXBRLViewerPlugin/viewer/src/js/number-matcher.js +2 -2
  60. iXBRLViewerPlugin/viewer/src/js/outline.js +2 -4
  61. iXBRLViewerPlugin/viewer/src/js/period.js +0 -1
  62. iXBRLViewerPlugin/viewer/src/js/report.js +68 -13
  63. iXBRLViewerPlugin/viewer/src/js/report.test.js +77 -6
  64. iXBRLViewerPlugin/viewer/src/js/reportset.js +33 -3
  65. iXBRLViewerPlugin/viewer/src/js/reportset.test.js +32 -6
  66. iXBRLViewerPlugin/viewer/src/js/search.js +61 -35
  67. iXBRLViewerPlugin/viewer/src/js/search.test.js +8 -5
  68. iXBRLViewerPlugin/viewer/src/js/summary.js +28 -2
  69. iXBRLViewerPlugin/viewer/src/js/summary.test.js +52 -14
  70. iXBRLViewerPlugin/viewer/src/js/tableExport.js +3 -3
  71. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.js +34 -0
  72. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.test.js +32 -0
  73. iXBRLViewerPlugin/viewer/src/js/theme.js +49 -0
  74. iXBRLViewerPlugin/viewer/src/js/unit.js +73 -2
  75. iXBRLViewerPlugin/viewer/src/js/unit.test.js +14 -3
  76. iXBRLViewerPlugin/viewer/src/js/util.js +21 -18
  77. iXBRLViewerPlugin/viewer/src/js/util.test.js +1 -0
  78. iXBRLViewerPlugin/viewer/src/js/utr.js +27 -0
  79. iXBRLViewerPlugin/viewer/src/js/viewer.js +40 -29
  80. iXBRLViewerPlugin/viewer/src/js/viewerOptions.js +0 -2
  81. iXBRLViewerPlugin/viewer/src/less/accordian.less +8 -4
  82. iXBRLViewerPlugin/viewer/src/less/block-list.less +12 -6
  83. iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +2 -2
  84. iXBRLViewerPlugin/viewer/src/less/chart.less +8 -5
  85. iXBRLViewerPlugin/viewer/src/less/colours-dark-mode.less +40 -0
  86. iXBRLViewerPlugin/viewer/src/less/colours.less +28 -21
  87. iXBRLViewerPlugin/viewer/src/less/common.less +1 -1
  88. iXBRLViewerPlugin/viewer/src/less/components.less +3 -3
  89. iXBRLViewerPlugin/viewer/src/less/core.less +2 -0
  90. iXBRLViewerPlugin/viewer/src/less/dialog.less +13 -10
  91. iXBRLViewerPlugin/viewer/src/less/form-controls.less +33 -11
  92. iXBRLViewerPlugin/viewer/src/less/inspector.less +556 -300
  93. iXBRLViewerPlugin/viewer/src/less/loader.less +2 -2
  94. iXBRLViewerPlugin/viewer/src/less/menu.less +33 -15
  95. iXBRLViewerPlugin/viewer/src/less/summary.less +16 -6
  96. iXBRLViewerPlugin/viewer/src/less/tabs.less +5 -5
  97. iXBRLViewerPlugin/viewer/src/less/text-mixins.less +2 -1
  98. iXBRLViewerPlugin/viewer/src/less/validation-report.less +1 -1
  99. iXBRLViewerPlugin/viewer/src/less/viewer.less +30 -18
  100. iXBRLViewerPlugin/viewer/webpack.common.js +19 -9
  101. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/METADATA +41 -14
  102. ixbrl_viewer-1.4.50.dist-info/RECORD +197 -0
  103. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/WHEEL +1 -1
  104. tests/puppeteer/framework/page_objects/doc_frame.js +3 -3
  105. tests/puppeteer/framework/page_objects/fact_details_panel.js +29 -1
  106. tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
  107. tests/puppeteer/tests/fact_properties.test.js +10 -4
  108. tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +117 -51
  109. iXBRLViewerPlugin/viewer/src/js/interact.min.js +0 -6
  110. ixbrl_viewer-1.4.21.dist-info/RECORD +0 -166
  111. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/LICENSE +0 -0
  112. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/NOTICE +0 -0
  113. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/entry_points.txt +0 -0
  114. {ixbrl_viewer-1.4.21.dist-info → ixbrl_viewer-1.4.50.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,8 @@ import { DocumentSummary } from "./summary.js";
6
6
  function testConcept(typedDomainElement) {
7
7
  return {
8
8
  isTypedDimension: () => typedDomainElement !== undefined,
9
- typedDomainElement: () => typedDomainElement
9
+ typedDomainElement: () => typedDomainElement,
10
+ hasDefinition: true,
10
11
  }
11
12
  }
12
13
 
@@ -17,16 +18,17 @@ function testFact(conceptName, dimensions) {
17
18
  }
18
19
  }
19
20
 
20
- function testReportSet(concepts, facts, documents) {
21
+ function testReportSet(concepts, facts, documents, softwareCredits) {
21
22
  const report = {
22
23
  getConcept: conceptName => concepts[conceptName],
23
- localDocuments: () => documents
24
+ localDocuments: () => documents,
24
25
  }
25
26
  for (const f of facts) {
26
27
  f.report = report;
27
28
  }
28
29
  return {
29
30
  facts: () => facts,
31
+ getSoftwareCredits: () => softwareCredits,
30
32
  reports: [ report ]
31
33
  }
32
34
  }
@@ -34,7 +36,7 @@ function testReportSet(concepts, facts, documents) {
34
36
  describe("Facts summary", () => {
35
37
 
36
38
  test("no facts", () => {
37
- const reportSet = testReportSet({}, [], {});
39
+ const reportSet = testReportSet({}, [], {}, []);
38
40
  const summary = new DocumentSummary(reportSet);
39
41
 
40
42
  expect(summary.totalFacts()).toBe(0);
@@ -43,9 +45,9 @@ describe("Facts summary", () => {
43
45
  test("multiple facts", () => {
44
46
  const facts = [];
45
47
  for (let i = 0; i < 10; i++) {
46
- facts.push(testFact("eg:Concept1", {}));
48
+ facts.push(testFact("eg:Concept1", {}, []));
47
49
  }
48
- const reportSet = testReportSet({}, facts, {});
50
+ const reportSet = testReportSet({}, facts, {}, []);
49
51
  const summary = new DocumentSummary(reportSet);
50
52
 
51
53
  expect(summary.totalFacts()).toBe(10);
@@ -55,7 +57,7 @@ describe("Facts summary", () => {
55
57
  const conceptName = "eg:Concept1";
56
58
  const fact1 = testFact(conceptName, {});
57
59
  const fact2 = testFact(conceptName, {});
58
- const reportSet = testReportSet({}, [fact1, fact2], {});
60
+ const reportSet = testReportSet({}, [fact1, fact2], {}, []);
59
61
  const summary = new DocumentSummary(reportSet);
60
62
 
61
63
  expect(summary.totalFacts()).toBe(2);
@@ -66,7 +68,7 @@ describe("Facts summary", () => {
66
68
  describe("Tags summary", () => {
67
69
 
68
70
  test("no tags", () => {
69
- const reportSet = testReportSet({}, [], {})
71
+ const reportSet = testReportSet({}, [], {}, [])
70
72
  const summary = new DocumentSummary(reportSet);
71
73
 
72
74
  expect(summary.tagCounts()).toEqual(new Map());
@@ -76,7 +78,7 @@ describe("Tags summary", () => {
76
78
  const fact1 = testFact("eg:Concept1", {});
77
79
  const fact2 = testFact("eg:Concept2", {});
78
80
  const fact3 = testFact("xz:Concept1", {});
79
- const reportSet = testReportSet({}, [fact1, fact2, fact3], {})
81
+ const reportSet = testReportSet({}, [fact1, fact2, fact3], {}, [])
80
82
  const summary = new DocumentSummary(reportSet);
81
83
 
82
84
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
@@ -105,7 +107,7 @@ describe("Tags summary", () => {
105
107
  const concepts = {
106
108
  [dimension]: testConcept(),
107
109
  }
108
- const reportSet = testReportSet(concepts, [fact], {})
110
+ const reportSet = testReportSet(concepts, [fact], {}, [])
109
111
  const summary = new DocumentSummary(reportSet);
110
112
 
111
113
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
@@ -135,7 +137,7 @@ describe("Tags summary", () => {
135
137
  [dimension2]: testConcept(),
136
138
  [dimension3]: testConcept(),
137
139
  }
138
- const reportSet = testReportSet(concepts, [fact], {})
140
+ const reportSet = testReportSet(concepts, [fact], {}, [])
139
141
  const summary = new DocumentSummary(reportSet);
140
142
 
141
143
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
@@ -176,7 +178,7 @@ describe("Tags summary", () => {
176
178
  const concepts = {
177
179
  [dimension]: testConcept(typedDomain),
178
180
  }
179
- const reportSet = testReportSet(concepts, [fact], {})
181
+ const reportSet = testReportSet(concepts, [fact], {}, [])
180
182
  const summary = new DocumentSummary(reportSet);
181
183
 
182
184
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
@@ -200,7 +202,7 @@ describe("Files summary", () => {
200
202
 
201
203
  test("no files", () => {
202
204
  const documentData = {}
203
- const reportSet = testReportSet({}, [], documentData);
205
+ const reportSet = testReportSet({}, [], documentData, []);
204
206
  const summary = new DocumentSummary(reportSet);
205
207
 
206
208
  expect(summary.getLocalDocuments()).toEqual({
@@ -228,7 +230,7 @@ describe("Files summary", () => {
228
230
  'labelLinkbase.xml': ['labelLinkbase'],
229
231
  'unrecognizedLinkbase.xml': ['unrecognizedLinkbase'],
230
232
  }
231
- const reportSet = testReportSet({}, [], documentData);
233
+ const reportSet = testReportSet({}, [], documentData, []);
232
234
  const summary = new DocumentSummary(reportSet);
233
235
 
234
236
  expect(summary.getLocalDocuments()).toEqual({
@@ -243,3 +245,39 @@ describe("Files summary", () => {
243
245
  });
244
246
  });
245
247
  });
248
+
249
+ describe("Software credit", () => {
250
+
251
+ test("no credits", () => {
252
+ const softwareCredits = []
253
+ const documentData = {}
254
+ const reportSet = testReportSet({}, [], documentData, softwareCredits);
255
+ const summary = new DocumentSummary(reportSet);
256
+
257
+ expect(summary.getSoftwareCredits()).toEqual([]);
258
+ });
259
+
260
+ test("one credit", () => {
261
+ const softwareCredits = ["Example credit text"]
262
+ const documentData = {}
263
+ const reportSet = testReportSet({}, [], documentData, softwareCredits);
264
+ const summary = new DocumentSummary(reportSet);
265
+
266
+ expect(summary.getSoftwareCredits()).toEqual(["Example credit text"]);
267
+ });
268
+
269
+ test("multiple credits", () => {
270
+ const softwareCredits = [
271
+ "Example credit text A",
272
+ "Example credit text B",
273
+ ]
274
+ const documentData = {}
275
+ const reportSet = testReportSet({}, [], documentData, softwareCredits);
276
+ const summary = new DocumentSummary(reportSet);
277
+
278
+ expect(summary.getSoftwareCredits()).toEqual([
279
+ "Example credit text A",
280
+ "Example credit text B",
281
+ ]);
282
+ });
283
+ });
@@ -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
- if (table.find(".ixbrl-element").length > 0) {
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(() => exporter.exportTable());
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 { measureLabel, NAMESPACE_ISO4217 } from "./util";
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:ZAR');
65
- expect(unit.label()).toEqual('ZAR');
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|;)-(?:sec|esef)-ix-hidden:\s*([^\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
- }
@@ -147,6 +147,7 @@ describe("Regex escape", () => {
147
147
 
148
148
  describe("Get IX Hidden Link Style", () => {
149
149
  it.each([
150
+ ["-ix-hidden:123", "123"],
150
151
  ["-sec-ix-hidden:123", "123"],
151
152
  ["-esef-ix-hidden:123", "123"],
152
153
  ["-xxx-ix-hidden:123", null],
@@ -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();