ixbrl-viewer 1.4.1__py3-none-any.whl → 1.4.86__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.
Files changed (190) hide show
  1. iXBRLViewerPlugin/__init__.py +231 -127
  2. iXBRLViewerPlugin/_version.py +33 -3
  3. iXBRLViewerPlugin/constants.py +96 -2
  4. iXBRLViewerPlugin/featureConfig.py +8 -1
  5. iXBRLViewerPlugin/iXBRLViewer.py +356 -214
  6. iXBRLViewerPlugin/plugin.py +12 -0
  7. iXBRLViewerPlugin/ui.py +81 -50
  8. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
  9. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt +12 -5
  10. iXBRLViewerPlugin/viewer/i18next-parser.config.js +1 -1
  11. iXBRLViewerPlugin/viewer/src/data/utr.json +1244 -0
  12. iXBRLViewerPlugin/viewer/src/html/fact-details.html +69 -38
  13. iXBRLViewerPlugin/viewer/src/html/footer-logo.html +4 -0
  14. iXBRLViewerPlugin/viewer/src/html/footnote-details.html +2 -2
  15. iXBRLViewerPlugin/viewer/src/html/inspector.html +352 -197
  16. iXBRLViewerPlugin/viewer/src/i18n/cy/balancetypes.json +1 -0
  17. iXBRLViewerPlugin/viewer/src/i18n/cy/currencies.json +13 -0
  18. iXBRLViewerPlugin/viewer/src/i18n/cy/datatypes.json +9 -0
  19. iXBRLViewerPlugin/viewer/src/i18n/cy/labelroles.json +24 -0
  20. iXBRLViewerPlugin/viewer/src/i18n/cy/referenceparts.json +10 -0
  21. iXBRLViewerPlugin/viewer/src/i18n/cy/scale.json +16 -0
  22. iXBRLViewerPlugin/viewer/src/i18n/cy/tooltips.json +17 -0
  23. iXBRLViewerPlugin/viewer/src/i18n/cy/translation.json +179 -0
  24. iXBRLViewerPlugin/viewer/src/i18n/da/balancetypes.json +4 -0
  25. iXBRLViewerPlugin/viewer/src/i18n/da/currencies.json +13 -0
  26. iXBRLViewerPlugin/viewer/src/i18n/da/datatypes.json +9 -0
  27. iXBRLViewerPlugin/viewer/src/i18n/da/labelroles.json +24 -0
  28. iXBRLViewerPlugin/viewer/src/i18n/da/referenceparts.json +10 -0
  29. iXBRLViewerPlugin/viewer/src/i18n/da/scale.json +15 -0
  30. iXBRLViewerPlugin/viewer/src/i18n/da/tooltips.json +17 -0
  31. iXBRLViewerPlugin/viewer/src/i18n/da/translation.json +179 -0
  32. iXBRLViewerPlugin/viewer/src/i18n/de/balancetypes.json +4 -0
  33. iXBRLViewerPlugin/viewer/src/i18n/de/currencies.json +13 -0
  34. iXBRLViewerPlugin/viewer/src/i18n/de/datatypes.json +9 -0
  35. iXBRLViewerPlugin/viewer/src/i18n/de/labelroles.json +24 -0
  36. iXBRLViewerPlugin/viewer/src/i18n/de/referenceparts.json +10 -0
  37. iXBRLViewerPlugin/viewer/src/i18n/de/scale.json +15 -0
  38. iXBRLViewerPlugin/viewer/src/i18n/de/tooltips.json +17 -0
  39. iXBRLViewerPlugin/viewer/src/i18n/de/translation.json +179 -0
  40. iXBRLViewerPlugin/viewer/src/i18n/en/balancetypes.json +4 -0
  41. iXBRLViewerPlugin/viewer/src/i18n/en/datatypes.json +10 -0
  42. iXBRLViewerPlugin/viewer/src/i18n/en/labelroles.json +4 -0
  43. iXBRLViewerPlugin/viewer/src/i18n/en/scale.json +16 -0
  44. iXBRLViewerPlugin/viewer/src/i18n/en/tooltips.json +17 -0
  45. iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +73 -23
  46. iXBRLViewerPlugin/viewer/src/i18n/es/balancetypes.json +4 -0
  47. iXBRLViewerPlugin/viewer/src/i18n/es/datatypes.json +10 -0
  48. iXBRLViewerPlugin/viewer/src/i18n/es/labelroles.json +24 -0
  49. iXBRLViewerPlugin/viewer/src/i18n/es/scale.json +16 -0
  50. iXBRLViewerPlugin/viewer/src/i18n/es/tooltips.json +17 -0
  51. iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +87 -37
  52. iXBRLViewerPlugin/viewer/src/i18n/fr/balancetypes.json +4 -0
  53. iXBRLViewerPlugin/viewer/src/i18n/fr/currencies.json +13 -0
  54. iXBRLViewerPlugin/viewer/src/i18n/fr/datatypes.json +9 -0
  55. iXBRLViewerPlugin/viewer/src/i18n/fr/labelroles.json +24 -0
  56. iXBRLViewerPlugin/viewer/src/i18n/fr/referenceparts.json +10 -0
  57. iXBRLViewerPlugin/viewer/src/i18n/fr/scale.json +15 -0
  58. iXBRLViewerPlugin/viewer/src/i18n/fr/tooltips.json +17 -0
  59. iXBRLViewerPlugin/viewer/src/i18n/fr/translation.json +179 -0
  60. iXBRLViewerPlugin/viewer/src/i18n/nl/balancetypes.json +4 -0
  61. iXBRLViewerPlugin/viewer/src/i18n/nl/currencies.json +13 -0
  62. iXBRLViewerPlugin/viewer/src/i18n/nl/datatypes.json +9 -0
  63. iXBRLViewerPlugin/viewer/src/i18n/nl/labelroles.json +24 -0
  64. iXBRLViewerPlugin/viewer/src/i18n/nl/referenceparts.json +10 -0
  65. iXBRLViewerPlugin/viewer/src/i18n/nl/scale.json +15 -0
  66. iXBRLViewerPlugin/viewer/src/i18n/nl/tooltips.json +17 -0
  67. iXBRLViewerPlugin/viewer/src/i18n/nl/translation.json +179 -0
  68. iXBRLViewerPlugin/viewer/src/i18n/uk/balancetypes.json +4 -0
  69. iXBRLViewerPlugin/viewer/src/i18n/uk/currencies.json +13 -0
  70. iXBRLViewerPlugin/viewer/src/i18n/uk/datatypes.json +9 -0
  71. iXBRLViewerPlugin/viewer/src/i18n/uk/labelroles.json +24 -0
  72. iXBRLViewerPlugin/viewer/src/i18n/uk/referenceparts.json +10 -0
  73. iXBRLViewerPlugin/viewer/src/i18n/uk/scale.json +15 -0
  74. iXBRLViewerPlugin/viewer/src/i18n/uk/tooltips.json +17 -0
  75. iXBRLViewerPlugin/viewer/src/i18n/uk/translation.json +179 -0
  76. iXBRLViewerPlugin/viewer/src/icons/calculator.svg +13 -0
  77. iXBRLViewerPlugin/viewer/src/icons/circle-cross.svg +11 -0
  78. iXBRLViewerPlugin/viewer/src/icons/circle-tick.svg +11 -0
  79. iXBRLViewerPlugin/viewer/src/icons/dark-mode.svg +4 -0
  80. iXBRLViewerPlugin/viewer/src/icons/dimension.svg +1 -5
  81. iXBRLViewerPlugin/viewer/src/icons/member.svg +2 -5
  82. iXBRLViewerPlugin/viewer/src/icons/multi-tag.svg +10 -0
  83. iXBRLViewerPlugin/viewer/src/img/arelle-dark.svg +179 -0
  84. iXBRLViewerPlugin/viewer/src/img/inline-viewer-dark.svg +59 -0
  85. iXBRLViewerPlugin/viewer/src/js/accordian.js +5 -4
  86. iXBRLViewerPlugin/viewer/src/js/aspect.js +29 -10
  87. iXBRLViewerPlugin/viewer/src/js/aspect.test.js +40 -31
  88. iXBRLViewerPlugin/viewer/src/js/balance.js +14 -0
  89. iXBRLViewerPlugin/viewer/src/js/calculation.js +213 -0
  90. iXBRLViewerPlugin/viewer/src/js/calculation.test.js +306 -0
  91. iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +187 -0
  92. iXBRLViewerPlugin/viewer/src/js/chart.js +26 -24
  93. iXBRLViewerPlugin/viewer/src/js/chart.test.js +10 -9
  94. iXBRLViewerPlugin/viewer/src/js/concept.js +37 -4
  95. iXBRLViewerPlugin/viewer/src/js/concept.test.js +30 -6
  96. iXBRLViewerPlugin/viewer/src/js/datatype.js +20 -0
  97. iXBRLViewerPlugin/viewer/src/js/datatype.test.js +62 -0
  98. iXBRLViewerPlugin/viewer/src/js/dialog.js +6 -4
  99. iXBRLViewerPlugin/viewer/src/js/docOrderIndex.js +7 -7
  100. iXBRLViewerPlugin/viewer/src/js/fact.js +156 -59
  101. iXBRLViewerPlugin/viewer/src/js/fact.test.js +160 -29
  102. iXBRLViewerPlugin/viewer/src/js/factset.js +64 -15
  103. iXBRLViewerPlugin/viewer/src/js/factset.test.js +102 -31
  104. iXBRLViewerPlugin/viewer/src/js/footnote.js +8 -2
  105. iXBRLViewerPlugin/viewer/src/js/index.js +11 -3
  106. iXBRLViewerPlugin/viewer/src/js/inspector.js +747 -221
  107. iXBRLViewerPlugin/viewer/src/js/inspector.test.js +143 -25
  108. iXBRLViewerPlugin/viewer/src/js/interval.js +70 -0
  109. iXBRLViewerPlugin/viewer/src/js/interval.test.js +153 -0
  110. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.js +391 -262
  111. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.test.js +134 -20
  112. iXBRLViewerPlugin/viewer/src/js/ixnode.js +1 -1
  113. iXBRLViewerPlugin/viewer/src/js/menu.js +25 -7
  114. iXBRLViewerPlugin/viewer/src/js/number-matcher.js +7 -3
  115. iXBRLViewerPlugin/viewer/src/js/number-matcher.test.js +4 -0
  116. iXBRLViewerPlugin/viewer/src/js/outline.js +34 -13
  117. iXBRLViewerPlugin/viewer/src/js/outline.test.js +97 -91
  118. iXBRLViewerPlugin/viewer/src/js/period.js +0 -1
  119. iXBRLViewerPlugin/viewer/src/js/report.js +260 -351
  120. iXBRLViewerPlugin/viewer/src/js/report.test.js +95 -27
  121. iXBRLViewerPlugin/viewer/src/js/reportset.js +264 -0
  122. iXBRLViewerPlugin/viewer/src/js/reportset.test.js +357 -0
  123. iXBRLViewerPlugin/viewer/src/js/search.js +72 -38
  124. iXBRLViewerPlugin/viewer/src/js/search.test.js +184 -84
  125. iXBRLViewerPlugin/viewer/src/js/summary.js +34 -8
  126. iXBRLViewerPlugin/viewer/src/js/summary.test.js +69 -25
  127. iXBRLViewerPlugin/viewer/src/js/tableExport.js +9 -9
  128. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.js +34 -0
  129. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.test.js +32 -0
  130. iXBRLViewerPlugin/viewer/src/js/test-utils.js +46 -0
  131. iXBRLViewerPlugin/viewer/src/js/theme.js +50 -0
  132. iXBRLViewerPlugin/viewer/src/js/unit.js +90 -32
  133. iXBRLViewerPlugin/viewer/src/js/unit.test.js +62 -25
  134. iXBRLViewerPlugin/viewer/src/js/util.js +94 -0
  135. iXBRLViewerPlugin/viewer/src/js/util.test.js +33 -1
  136. iXBRLViewerPlugin/viewer/src/js/utr.js +27 -0
  137. iXBRLViewerPlugin/viewer/src/js/viewer.js +205 -181
  138. iXBRLViewerPlugin/viewer/src/js/viewerOptions.js +0 -2
  139. iXBRLViewerPlugin/viewer/src/less/accordian.less +10 -6
  140. iXBRLViewerPlugin/viewer/src/less/block-list.less +16 -5
  141. iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +83 -0
  142. iXBRLViewerPlugin/viewer/src/less/chart.less +8 -5
  143. iXBRLViewerPlugin/viewer/src/less/colours-dark-mode.less +40 -0
  144. iXBRLViewerPlugin/viewer/src/less/colours.less +32 -20
  145. iXBRLViewerPlugin/viewer/src/less/common.less +3 -3
  146. iXBRLViewerPlugin/viewer/src/less/components.less +6 -4
  147. iXBRLViewerPlugin/viewer/src/less/core.less +2 -0
  148. iXBRLViewerPlugin/viewer/src/less/dialog.less +21 -14
  149. iXBRLViewerPlugin/viewer/src/less/form-controls.less +33 -11
  150. iXBRLViewerPlugin/viewer/src/less/inspector.less +1045 -726
  151. iXBRLViewerPlugin/viewer/src/less/loader.less +2 -2
  152. iXBRLViewerPlugin/viewer/src/less/menu.less +33 -15
  153. iXBRLViewerPlugin/viewer/src/less/summary.less +16 -6
  154. iXBRLViewerPlugin/viewer/src/less/tabs.less +9 -9
  155. iXBRLViewerPlugin/viewer/src/less/text-block-viewer.less +2 -0
  156. iXBRLViewerPlugin/viewer/src/less/text-mixins.less +2 -1
  157. iXBRLViewerPlugin/viewer/src/less/validation-report.less +2 -3
  158. iXBRLViewerPlugin/viewer/src/less/viewer.less +105 -74
  159. iXBRLViewerPlugin/viewer/webpack.common.js +19 -9
  160. iXBRLViewerPlugin/xhtmlserialize.py +59 -45
  161. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/METADATA +181 -50
  162. ixbrl_viewer-1.4.86.dist-info/RECORD +217 -0
  163. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/WHEEL +1 -1
  164. ixbrl_viewer-1.4.1.dist-info/LICENSE → ixbrl_viewer-1.4.86.dist-info/licenses/LICENSE.md +8 -14
  165. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/top_level.txt +0 -1
  166. iXBRLViewerPlugin/viewer/src/js/calculations.js +0 -111
  167. iXBRLViewerPlugin/viewer/src/js/interact.min.js +0 -6
  168. ixbrl_viewer-1.4.1.dist-info/RECORD +0 -155
  169. tests/__init__.py +0 -0
  170. tests/puppeteer/framework/core_elements.js +0 -117
  171. tests/puppeteer/framework/page_objects/doc_frame.js +0 -105
  172. tests/puppeteer/framework/page_objects/fact_details_panel.js +0 -80
  173. tests/puppeteer/framework/page_objects/search_panel.js +0 -76
  174. tests/puppeteer/framework/page_objects/toolbar.js +0 -18
  175. tests/puppeteer/framework/utils.js +0 -3
  176. tests/puppeteer/framework/viewer_page.js +0 -103
  177. tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
  178. tests/puppeteer/test_filings/filing_documents_smoke_test.zip +0 -0
  179. tests/puppeteer/test_filings/highlights.zip +0 -0
  180. tests/puppeteer/tests/fact_properties.test.js +0 -78
  181. tests/puppeteer/tests/highlight.test.js +0 -186
  182. tests/puppeteer/tests/search.test.js +0 -86
  183. tests/puppeteer/tools/generate.sh +0 -15
  184. tests/unit_tests/__init__.py +0 -0
  185. tests/unit_tests/iXBRLViewerPlugin/__init__.py +0 -0
  186. tests/unit_tests/iXBRLViewerPlugin/mock_arelle.py +0 -39
  187. tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +0 -641
  188. tests/unit_tests/iXBRLViewerPlugin/test_xhtmlserialize.py +0 -310
  189. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/entry_points.txt +0 -0
  190. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info/licenses}/NOTICE +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,19 +18,26 @@ function testFact(conceptName, dimensions) {
17
18
  }
18
19
  }
19
20
 
20
- function testReport(concepts, facts, documents) {
21
- return {
21
+ function testReportSet(concepts, facts, documents, softwareCredits) {
22
+ const report = {
22
23
  getConcept: conceptName => concepts[conceptName],
24
+ localDocuments: () => documents,
25
+ }
26
+ for (const f of facts) {
27
+ f.report = report;
28
+ }
29
+ return {
23
30
  facts: () => facts,
24
- localDocuments: () => documents
31
+ getSoftwareCredits: () => softwareCredits,
32
+ reports: [ report ]
25
33
  }
26
34
  }
27
35
 
28
36
  describe("Facts summary", () => {
29
37
 
30
38
  test("no facts", () => {
31
- const report = testReport({}, [], {});
32
- const summary = new DocumentSummary(report);
39
+ const reportSet = testReportSet({}, [], {}, []);
40
+ const summary = new DocumentSummary(reportSet);
33
41
 
34
42
  expect(summary.totalFacts()).toBe(0);
35
43
  });
@@ -37,10 +45,10 @@ describe("Facts summary", () => {
37
45
  test("multiple facts", () => {
38
46
  const facts = [];
39
47
  for (let i = 0; i < 10; i++) {
40
- facts.push(testFact("eg:Concept1", {}));
48
+ facts.push(testFact("eg:Concept1", {}, []));
41
49
  }
42
- const report = testReport({}, facts, {});
43
- const summary = new DocumentSummary(report);
50
+ const reportSet = testReportSet({}, facts, {}, []);
51
+ const summary = new DocumentSummary(reportSet);
44
52
 
45
53
  expect(summary.totalFacts()).toBe(10);
46
54
  });
@@ -49,8 +57,8 @@ describe("Facts summary", () => {
49
57
  const conceptName = "eg:Concept1";
50
58
  const fact1 = testFact(conceptName, {});
51
59
  const fact2 = testFact(conceptName, {});
52
- const report = testReport({}, [fact1, fact2], {});
53
- const summary = new DocumentSummary(report);
60
+ const reportSet = testReportSet({}, [fact1, fact2], {}, []);
61
+ const summary = new DocumentSummary(reportSet);
54
62
 
55
63
  expect(summary.totalFacts()).toBe(2);
56
64
  });
@@ -60,8 +68,8 @@ describe("Facts summary", () => {
60
68
  describe("Tags summary", () => {
61
69
 
62
70
  test("no tags", () => {
63
- const report = testReport({}, [], {})
64
- const summary = new DocumentSummary(report);
71
+ const reportSet = testReportSet({}, [], {}, [])
72
+ const summary = new DocumentSummary(reportSet);
65
73
 
66
74
  expect(summary.tagCounts()).toEqual(new Map());
67
75
  });
@@ -70,8 +78,8 @@ describe("Tags summary", () => {
70
78
  const fact1 = testFact("eg:Concept1", {});
71
79
  const fact2 = testFact("eg:Concept2", {});
72
80
  const fact3 = testFact("xz:Concept1", {});
73
- const report = testReport({}, [fact1, fact2, fact3], {})
74
- const summary = new DocumentSummary(report);
81
+ const reportSet = testReportSet({}, [fact1, fact2, fact3], {}, [])
82
+ const summary = new DocumentSummary(reportSet);
75
83
 
76
84
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
77
85
  'eg': {
@@ -99,8 +107,8 @@ describe("Tags summary", () => {
99
107
  const concepts = {
100
108
  [dimension]: testConcept(),
101
109
  }
102
- const report = testReport(concepts, [fact], {})
103
- const summary = new DocumentSummary(report);
110
+ const reportSet = testReportSet(concepts, [fact], {}, [])
111
+ const summary = new DocumentSummary(reportSet);
104
112
 
105
113
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
106
114
  'eg': {
@@ -129,8 +137,8 @@ describe("Tags summary", () => {
129
137
  [dimension2]: testConcept(),
130
138
  [dimension3]: testConcept(),
131
139
  }
132
- const report = testReport(concepts, [fact], {})
133
- const summary = new DocumentSummary(report);
140
+ const reportSet = testReportSet(concepts, [fact], {}, [])
141
+ const summary = new DocumentSummary(reportSet);
134
142
 
135
143
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
136
144
  'ab': {
@@ -170,8 +178,8 @@ describe("Tags summary", () => {
170
178
  const concepts = {
171
179
  [dimension]: testConcept(typedDomain),
172
180
  }
173
- const report = testReport(concepts, [fact], {})
174
- const summary = new DocumentSummary(report);
181
+ const reportSet = testReportSet(concepts, [fact], {}, [])
182
+ const summary = new DocumentSummary(reportSet);
175
183
 
176
184
  expect(Object.fromEntries(summary.tagCounts())).toEqual({
177
185
  'eg': {
@@ -194,8 +202,8 @@ describe("Files summary", () => {
194
202
 
195
203
  test("no files", () => {
196
204
  const documentData = {}
197
- const report = testReport({}, [], documentData);
198
- const summary = new DocumentSummary(report);
205
+ const reportSet = testReportSet({}, [], documentData, []);
206
+ const summary = new DocumentSummary(reportSet);
199
207
 
200
208
  expect(summary.getLocalDocuments()).toEqual({
201
209
  inline: [],
@@ -222,8 +230,8 @@ describe("Files summary", () => {
222
230
  'labelLinkbase.xml': ['labelLinkbase'],
223
231
  'unrecognizedLinkbase.xml': ['unrecognizedLinkbase'],
224
232
  }
225
- const report = testReport({}, [], documentData);
226
- const summary = new DocumentSummary(report);
233
+ const reportSet = testReportSet({}, [], documentData, []);
234
+ const summary = new DocumentSummary(reportSet);
227
235
 
228
236
  expect(summary.getLocalDocuments()).toEqual({
229
237
  inline: ['docset', 'inline.htm'],
@@ -237,3 +245,39 @@ describe("Files summary", () => {
237
245
  });
238
246
  });
239
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,33 +1,33 @@
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
 
8
7
 
9
8
  export class TableExport {
10
- constructor(table, report) {
9
+ constructor(table, reportSet) {
11
10
  this._table = table;
12
- this._report = report;
11
+ this._reportSet = reportSet;
13
12
  }
14
13
 
15
- static addHandles(iframe, report) {
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
- const exporter = new TableExport(table, report);
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
  }
27
27
 
28
28
  _getRawTable() {
29
29
  const table = this._table;
30
- const report = this._report;
30
+ const reportSet = this._reportSet;
31
31
  let maxRowLength = 0;
32
32
  const rows = [];
33
33
  table.find("tr").each(function () {
@@ -44,7 +44,7 @@ export class TableExport {
44
44
  let fact = null;
45
45
  if (facts.length > 0) {
46
46
  const id = facts.first().data('ivids');
47
- fact = report.getItemById(id);
47
+ fact = reportSet.getItemById(id);
48
48
  }
49
49
  if (fact instanceof Fact) {
50
50
  const cell = { type: "fact", fact: fact};
@@ -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
+
@@ -10,3 +10,49 @@ export function TestInspector() {
10
10
  }
11
11
 
12
12
  TestInspector.prototype = Object.create(Inspector.prototype);
13
+
14
+ expect.extend({
15
+ toEqualDecimal(received, expected) {
16
+ const options = {
17
+ comment: 'decimal.js equality',
18
+ isNot: this.isNot,
19
+ promise: this.promise,
20
+ };
21
+ const pass = received.equals(expected);
22
+ const message = () =>
23
+ this.utils.matcherHint('toEqualDecimals', undefined, undefined, options) +
24
+ '\n\n' +
25
+ `Expected: ${this.isNot ? '(not) ' : ''}${this.utils.printExpected(new Decimal(expected))}\n` +
26
+ `Received: ${this.utils.printReceived(received)}`;
27
+
28
+ return {actual: received, message, pass};
29
+
30
+ }
31
+ });
32
+
33
+ export function createSimpleFact(id, concept, options=null) {
34
+ options = options || {};
35
+ return {
36
+ [id]: {
37
+ "a": {
38
+ "c": concept,
39
+ "u": options["unit"],
40
+ "p": options["period"],
41
+ },
42
+ "d": options["decimals"],
43
+ "v": options["value"]
44
+ }
45
+ };
46
+ }
47
+
48
+ export function createNumericFact(id, concept, unit, period, value, decimals) {
49
+ const options = {
50
+ "unit": unit,
51
+ "period": period,
52
+ "value": value
53
+ };
54
+ if (decimals !== undefined) {
55
+ options.decimals = decimals;
56
+ }
57
+ return createSimpleFact(id, concept, options);
58
+ }
@@ -0,0 +1,50 @@
1
+ // See COPYRIGHT.md for copyright information
2
+
3
+ import { IXBRL_VIEWER_DATASET_PREFIX, STORAGE_THEME } from "./util";
4
+
5
+ const DARK_THEME = 'dark';
6
+ const LIGHT_THEME = 'light';
7
+ const APP_THEME_DATASET_NAME = `${IXBRL_VIEWER_DATASET_PREFIX}Theme`;
8
+
9
+ function getHtmlElement() {
10
+ return document.querySelector('html');
11
+ }
12
+
13
+ export function getVariable(name) {
14
+ const html = getHtmlElement();
15
+ return getComputedStyle(html).getPropertyValue(name);
16
+ }
17
+
18
+ function setTheme(theme) {
19
+ const html = getHtmlElement();
20
+ html.dataset[APP_THEME_DATASET_NAME] = theme;
21
+ }
22
+
23
+ function getStoredTheme() {
24
+ return localStorage.getItem(STORAGE_THEME);
25
+ }
26
+
27
+ function storeTheme(theme) {
28
+ localStorage.setItem(STORAGE_THEME, theme);
29
+ }
30
+
31
+ export function getTheme() {
32
+ const html = getHtmlElement();
33
+ return html.dataset[APP_THEME_DATASET_NAME];
34
+ }
35
+
36
+ export function initializeTheme() {
37
+ const storedTheme = getStoredTheme();
38
+ if (storedTheme !== null) {
39
+ setTheme(storedTheme);
40
+ } else {
41
+ setTheme(window.matchMedia(`(prefers-color-scheme: ${DARK_THEME})`).matches ? DARK_THEME : LIGHT_THEME);
42
+ }
43
+ }
44
+
45
+ export function toggleTheme() {
46
+ const currentTheme = getTheme();
47
+ const newTheme = currentTheme === LIGHT_THEME ? DARK_THEME : LIGHT_THEME;
48
+ setTheme(newTheme);
49
+ storeTheme(newTheme);
50
+ }
@@ -1,29 +1,70 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
+ import { NAMESPACE_ISO4217 } from "./util";
3
4
  import i18next from "i18next";
4
- import {titleCase} from "./util";
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 for currencies
16
+ const keyi18n = `currencies:unitFormat${qname.localname}`;
17
+ if (i18next.exists(keyi18n)) {
18
+ return i18next.t(keyi18n);
19
+ }
20
+ }
21
+ // Pick symbol from UTR ...
22
+ const utrEntry = utr.get(qname);
23
+ if (utrEntry !== undefined) {
24
+ if (utrEntry.symbol !== undefined) {
25
+ // ... but disambiguate "$" symbol
26
+ return utrEntry.symbol == '$' ? `${qname.localname} $` : utrEntry.symbol;
27
+ }
28
+ // Fall back to name
29
+ return utrEntry.name;
30
+ }
31
+ // Otherwise the measure is not in the UTR so fallback to unitId
32
+ if (measure.includes(':')) {
33
+ return measure.split(':')[1];
34
+ }
35
+ return measure;
36
+ }
37
+
38
+ export function measureName(report, measure) {
39
+ const qname = report.qname(measure);
40
+ const utrEntry = utr.get(qname);
41
+ return utrEntry?.name;
42
+ }
5
43
 
6
44
  export class Unit {
7
-
8
- constructor(report, unitKey) {
9
- this._report = report;
45
+ constructor(reportSet, unitKey) {
46
+ this._reportSet = reportSet;
10
47
  this._value = unitKey;
11
48
  const split = unitKey
12
49
  .split(/[()]/ig).join('') // TODO: replace with .replaceAll(/[()]/ig,'') when no longer supporting node 14
13
50
  .split('/');
14
51
  this._numerators = split[0].split('*');
15
52
  this._denominators = split.length > 1 ? split[1].split('*') : [];
16
- this._isMonetary = Boolean(this._numerators.find(n => this._report.qname(n).namespace === "http://www.xbrl.org/2003/iso4217"));
17
- this._label = split.map(x => {
18
- const part = x.split('*').map(y => {
19
- return titleCase(y.split(':')[1]);
20
- }).join('*');
53
+ this._isMonetary =
54
+ this._denominators.length === 0 &&
55
+ this._numerators.length === 1 &&
56
+ this._reportSet.qname(this._numerators[0]).namespace === NAMESPACE_ISO4217;
57
+ this._label = split
58
+ .map(measure => {
59
+ const part = measure
60
+ .split('*')
61
+ .map(x => measureLabel(this._reportSet, x))
62
+ .join('*');
21
63
  return part.includes('*') ? `(${part})` : part;
22
- }).join('/');
23
- this._measure = this._numerators[0];
64
+ })
65
+ .join('/');
24
66
  }
25
67
 
26
-
27
68
  /**
28
69
  * Returns whether any of the numerators are an iso4217 monetary measure.
29
70
  * @return {Boolean}
@@ -41,32 +82,49 @@ export class Unit {
41
82
  }
42
83
 
43
84
  /**
44
- * Returns the qname of the first numerator in the unit
45
- * @return {String} QName string of a measure
85
+ * Returns the OIM format string representing the unit
86
+ * @return {String} OIM format unit string
46
87
  */
47
- measure() {
48
- return this._measure;
88
+ value() {
89
+ return this._value;
49
90
  }
50
91
 
51
- /**
52
- * Returns a readable label representing the first numerator in the unit
53
- * @return {String} Label representing measure
54
- */
55
- measureLabel() {
56
- const measure = this.measure();
57
- const qname = this._report.qname(measure);
58
- if (qname.namespace === "http://www.xbrl.org/2003/iso4217") {
59
- return i18next.t(`currencies:unitFormat${qname.localname}`, {defaultValue: qname.localname});
92
+ measureHTML(m) {
93
+ const span = document.createElement("span");
94
+ const name = measureName(this._reportSet, m);
95
+ if (name !== undefined) {
96
+ span.setAttribute("title", name);
97
+ span.classList.add("measure");
60
98
  }
61
- return measure;
99
+ span.append(document.createTextNode(measureLabel(this._reportSet, m)));
100
+ return span;
62
101
  }
63
102
 
64
- /**
65
- * Returns the OIM format string representing the unit
66
- * @return {String} OIM format unit string
67
- */
68
- value() {
69
- return this._value;
103
+ partsHTML(parts) {
104
+ const span = document.createElement("span");
105
+ if (parts.length > 1) {
106
+ span.appendChild(document.createTextNode("("));
107
+ }
108
+ for (const [i, m] of parts.entries()) {
109
+ span.appendChild(this.measureHTML(m));
110
+ if (i < parts.length - 1) {
111
+ span.appendChild(document.createTextNode(" * "))
112
+ }
113
+ }
114
+ if (parts.length > 1) {
115
+ span.appendChild(document.createTextNode(")"));
116
+ }
117
+ return span;
118
+ }
119
+
120
+ html() {
121
+ const span = document.createElement("span");
122
+ span.append(this.partsHTML(this._numerators));
123
+ if (this._denominators.length > 0) {
124
+ span.append(document.createTextNode(" / "));
125
+ span.append(this.partsHTML(this._denominators));
126
+ }
127
+ return span;
70
128
  }
71
129
  }
72
130