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
@@ -1,13 +1,16 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
3
  import { Fact } from "./fact.js";
4
- import { iXBRLReport } from "./report.js";
4
+ import { ReportSet } from "./reportset.js";
5
5
  import { TestInspector } from "./test-utils.js";
6
+ import { NAMESPACE_ISO4217 } from "./util";
6
7
 
7
8
  var testReportData = {
8
9
  "prefixes": {
9
10
  "eg": "http://www.example.com",
10
- "iso4217": "http://www.xbrl.org/2003/iso4217",
11
+ "aa": "http://www.example.com/aa",
12
+ "zz": "http://www.example.com/zz",
13
+ "iso4217": NAMESPACE_ISO4217,
11
14
  "e": "http://example.com/entity",
12
15
  },
13
16
  "concepts": {
@@ -32,6 +35,14 @@ var testReportData = {
32
35
  }
33
36
  }
34
37
  },
38
+ "eg:TextBlockConcept1": {
39
+ "labels": {
40
+ "std": {
41
+ "en": "Text block concept"
42
+ }
43
+ },
44
+ "t": true,
45
+ },
35
46
  "eg:EnumConcept": {
36
47
  "labels": {
37
48
  "std": {
@@ -48,6 +59,30 @@ var testReportData = {
48
59
  },
49
60
  "d": "e"
50
61
  },
62
+ "eg:Dimension2": {
63
+ "labels": {
64
+ "std": {
65
+ "en": "Dimension Two"
66
+ }
67
+ },
68
+ "d": "e"
69
+ },
70
+ "zz:Axis1": {
71
+ "labels": {
72
+ "std": {
73
+ "en": "Axis One"
74
+ }
75
+ },
76
+ "d": "e"
77
+ },
78
+ "aa:Dimension3": {
79
+ "labels": {
80
+ "std": {
81
+ "en": "Dimension Three"
82
+ }
83
+ },
84
+ "d": "e"
85
+ },
51
86
  "eg:Member1": {
52
87
  "labels": {
53
88
  "std": {
@@ -62,6 +97,20 @@ var testReportData = {
62
97
  }
63
98
  }
64
99
  },
100
+ "eg:Member3": {
101
+ "labels": {
102
+ "std": {
103
+ "en": "Member Three"
104
+ }
105
+ }
106
+ },
107
+ "eg:Member4": {
108
+ "labels": {
109
+ "std": {
110
+ "en": "Member Four"
111
+ }
112
+ }
113
+ },
65
114
  "eg:UnlabelledMember": {
66
115
  "labels": {
67
116
  }
@@ -75,16 +124,16 @@ function testReport(facts, ixData) {
75
124
  // Deep copy of standing data
76
125
  var data = JSON.parse(JSON.stringify(testReportData));
77
126
  data.facts = facts;
78
- var report = new iXBRLReport(data);
79
- report.setIXNodeMap(ixData);
80
- return report;
127
+ var reportSet = new ReportSet(data);
128
+ reportSet.setIXNodeMap(ixData);
129
+ return reportSet.reports[0];
81
130
  }
82
131
 
83
132
  function testFact(factData, ixData) {
84
133
  factData.a = factData.a || {};
85
134
  factData.a.c = factData.a.c || 'eg:Concept1';
86
135
  ixData = ixData || {};
87
- return new Fact(testReport({"f1": factData}, {"f1": ixData }), "f1");
136
+ return new Fact(testReport({"f1": factData}, {"f1": ixData }), "f1", factData);
88
137
  }
89
138
 
90
139
  var insp = new TestInspector();
@@ -108,7 +157,6 @@ describe("Simple fact properties", () => {
108
157
  expect(f.isMonetaryValue()).toBeTruthy();
109
158
  expect(f.readableValue()).toEqual("US $ 1,000");
110
159
  expect(f.unit().value()).toEqual("iso4217:USD");
111
- expect(f.measure()).toEqual("iso4217:USD");
112
160
  expect(f.conceptQName().prefix).toEqual("eg");
113
161
  expect(f.conceptQName().localname).toEqual("Concept1");
114
162
  expect(f.conceptQName().namespace).toEqual("http://www.example.com");
@@ -128,9 +176,8 @@ describe("Simple fact properties", () => {
128
176
  expect(f.isNumeric()).toBeTruthy();
129
177
  expect(f.decimals()).toEqual(-3);
130
178
  expect(f.isMonetaryValue()).toBeFalsy();
131
- expect(f.readableValue()).toEqual("1,000 eg:USD");
179
+ expect(f.readableValue()).toEqual("1,000 USD");
132
180
  expect(f.unit().value()).toEqual("eg:USD");
133
- expect(f.measure()).toEqual("eg:USD");
134
181
  expect(f.conceptQName().prefix).toEqual("eg");
135
182
  expect(f.conceptQName().localname).toEqual("Concept1");
136
183
  expect(f.conceptQName().namespace).toEqual("http://www.example.com");
@@ -149,9 +196,8 @@ describe("Simple fact properties", () => {
149
196
  expect(f.decimals()).toBeUndefined();
150
197
  expect(f.isNumeric()).toBeTruthy();
151
198
  expect(f.isMonetaryValue()).toBeFalsy();
152
- expect(f.readableValue()).toEqual("1,000,000.0125 eg:USD");
199
+ expect(f.readableValue()).toEqual("1,000,000.0125 USD");
153
200
  expect(f.unit().value()).toEqual("eg:USD");
154
- expect(f.measure()).toEqual("eg:USD");
155
201
  expect(f.conceptQName().prefix).toEqual("eg");
156
202
  expect(f.conceptQName().localname).toEqual("Concept1");
157
203
  expect(f.conceptQName().namespace).toEqual("http://www.example.com");
@@ -539,7 +585,7 @@ describe("Readable value", () => {
539
585
  test("Other numeric", () => {
540
586
 
541
587
  expect(testFact({ "v": "10", d: -2, a: { u: "xbrli:foo" } }).readableValue())
542
- .toBe("10 xbrli:foo");
588
+ .toBe("10 foo");
543
589
 
544
590
  });
545
591
 
@@ -552,20 +598,43 @@ describe("Readable value", () => {
552
598
 
553
599
  });
554
600
 
555
- test("Strip HTML tags and normalise whitespace", () => {
601
+ test("Escaped string", () => {
556
602
 
557
603
  expect(testFact({ "v": "<b>foo</b>" }, {"escaped": true }).readableValue())
558
- .toBe("foo");
604
+ .toBe("<b>foo</b>");
559
605
 
560
606
  expect(testFact({ "v": " <b>foo</b>bar" }, {"escaped": true }).readableValue())
561
- .toBe("foobar");
607
+ .toBe(" <b>foo</b>bar");
562
608
 
563
609
  expect(testFact({ "v": "\u00a0<b>foo</b>" }, {"escaped": true }).readableValue())
610
+ .toBe("\u00a0<b>foo</b>");
611
+
612
+ });
613
+
614
+ test("Strip HTML tags and normalise whitespace", () => {
615
+
616
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<b>foo</b>" }, {"escaped": true }).readableValue())
617
+ .toBe("foo");
618
+
619
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": " <b>foo</b>bar" }, {"escaped": true }).readableValue())
620
+ .toBe("foobar");
621
+
622
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "\u00a0<b>foo</b>" }, {"escaped": true }).readableValue())
623
+ .toBe("foo");
624
+
625
+ });
626
+
627
+ test("Strip non-escaped text block facts", () => {
628
+
629
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "\u00a0<b>foo</b>" }, {"escaped": false }).readableValue())
630
+ .toBe("foo");
631
+
632
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "\u00a0<b>foo</b>" }, { }).readableValue())
564
633
  .toBe("foo");
565
634
 
566
635
  });
567
636
 
568
- test("Don't strip non-escaped facts", () => {
637
+ test("Don't strip non-text-block facts", () => {
569
638
 
570
639
  expect(testFact({ "v": "\u00a0<b>foo</b>" }, {"escaped": false }).readableValue())
571
640
  .toBe("\u00a0<b>foo</b>");
@@ -576,40 +645,40 @@ describe("Readable value", () => {
576
645
  });
577
646
 
578
647
  test("Detect and strip HTML tags - XHTML tags and attributes", () => {
579
- expect(testFact({ "v": "<xhtml:b>foo</xhtml:b>" }, {"escaped": true }).readableValue())
648
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<xhtml:b>foo</xhtml:b>" }, {"escaped": true }).readableValue())
580
649
  .toBe("foo");
581
650
 
582
- expect(testFact({ "v": '<xhtml:span style="font-weight: bold">foo</xhtml:span>' }, {"escaped": true }).readableValue())
651
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": '<xhtml:span style="font-weight: bold">foo</xhtml:span>' }, {"escaped": true }).readableValue())
583
652
  .toBe("foo");
584
653
  });
585
654
 
586
655
  test("Detect and strip HTML tags - check behaviour with invalid HTML", () => {
587
656
  /* Invalid HTML */
588
- expect(testFact({ "v": "<b:b:b>foo</b:b:b>" }, {"escaped": true }).readableValue())
657
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<b:b:b>foo</b:b:b>" }, {"escaped": true }).readableValue())
589
658
  .toBe("foo");
590
659
 
591
- expect(testFact({ "v": "<foo<bar>baz</bar>" }, {"escaped": true }).readableValue())
660
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<foo<bar>baz</bar>" }, {"escaped": true }).readableValue())
592
661
  .toBe("baz");
593
662
  });
594
663
 
595
664
  test("Text in consecutive inline elements should be contiguous", () => {
596
665
 
597
- expect(testFact({ "v": "<b>foo</b><i>bar</i>" }, {"escaped":true }).readableValue())
666
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<b>foo</b><i>bar</i>" }, {"escaped":true }).readableValue())
598
667
  .toBe("foobar");
599
668
 
600
669
  });
601
670
 
602
671
  test("Text in block/table elements should be separated.", () => {
603
672
 
604
- expect(testFact({ "v": "<p>foo</p><p>bar</p>" }, {"escaped":true }).readableValue())
673
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<p>foo</p><p>bar</p>" }, {"escaped":true }).readableValue())
605
674
  .toBe("foo bar");
606
675
 
607
676
  /* This should really return "foo bar", but we don't correctly detect
608
677
  * block tags in prefixed XHTML */
609
- expect(testFact({ "v": '<xhtml:p xmlns:xhtml="https://www.w3.org/1999/xhtml/">foo</xhtml:p><xhtml:p>bar</xhtml:p>' }, {"escaped":true }).readableValue())
678
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": '<xhtml:p xmlns:xhtml="https://www.w3.org/1999/xhtml/">foo</xhtml:p><xhtml:p>bar</xhtml:p>' }, {"escaped":true }).readableValue())
610
679
  .toBe("foobar");
611
680
 
612
- expect(testFact({ "v": "<table><tr><td>cell1</td><td>cell2</td></tr></table>" }, {"escaped":true })
681
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<table><tr><td>cell1</td><td>cell2</td></tr></table>" }, {"escaped":true })
613
682
  .readableValue())
614
683
  .toBe("cell1 cell2");
615
684
 
@@ -617,7 +686,7 @@ describe("Readable value", () => {
617
686
 
618
687
  test("Whitespace normalisation", () => {
619
688
 
620
- expect(testFact({ "v": "<p>bar foo</p> <p>bar</p>" }, {"escaped":true }).readableValue())
689
+ expect(testFact({ "a": { "c": "eg:TextBlockConcept1" }, "v": "<p>bar foo</p> <p>bar</p>" }, {"escaped":true }).readableValue())
621
690
  .toBe("bar foo bar");
622
691
 
623
692
  });
@@ -635,8 +704,7 @@ describe("Unit aspect handling", () => {
635
704
  expect(f.isNumeric()).toBeTruthy();
636
705
  expect(f.isMonetaryValue()).toBeFalsy();
637
706
  expect(f.unit()).toBeUndefined();
638
- expect(f.measure()).toBeUndefined();
639
- expect(f.measureLabel()).toBe("<NOUNIT>");
707
+ expect(f.unitLabel()).toBe("<NOUNIT>");
640
708
  });
641
709
 
642
710
  test("Non-numeric, no unit", () => {
@@ -647,7 +715,6 @@ describe("Unit aspect handling", () => {
647
715
  });
648
716
  expect(f.isNumeric()).toBeFalsy();
649
717
  expect(f.unit()).toBeUndefined();
650
- expect(f.measure()).toBeUndefined();
651
718
  });
652
719
  });
653
720
 
@@ -663,10 +730,13 @@ describe("Get Label", () => {
663
730
 
664
731
  test("Get standard label", () => {
665
732
  expect(f.getLabel("std")).toEqual("English label")
733
+ expect(f.getLabelAndLang("std").label).toEqual("English label")
734
+ expect(f.getLabelAndLang("std").lang).toEqual("en")
666
735
  });
667
736
 
668
737
  test("Get non-existent label", () => {
669
738
  expect(f.getLabel("doc")).toBeUndefined();
739
+ expect(f.getLabelAndLang("doc").label).toBeUndefined();
670
740
  });
671
741
 
672
742
  });
@@ -690,6 +760,68 @@ describe("Aspect methods", () => {
690
760
  });
691
761
  });
692
762
 
763
+ describe("Aspect sorting", () => {
764
+ var f = testFact({
765
+ "d": -3,
766
+ "v": 1000,
767
+ "a": {
768
+ "c": "eg:Concept1",
769
+ "u": "iso4217:USD",
770
+ "p": "2018-01-01/2019-01-01",
771
+ "eg:Dimension2": "eg:Member1",
772
+ "aa:Dimension3": "eg:Member2",
773
+ "zz:Axis1": "eg:Member3",
774
+ "eg:Dimension1": "eg:Member4"
775
+ }});
776
+ test("Fact.aspects() is sorted", () => {
777
+ expect(f.aspects().length).toEqual(7);
778
+ expect(f.aspects().filter(a => a.isTaxonomyDefined()).length).toEqual(4);
779
+
780
+ expect(f.aspects().filter(a => a.isTaxonomyDefined())[0].name()).toEqual("aa:Dimension3");
781
+ expect(f.aspects().filter(a => a.isTaxonomyDefined())[0].label()).toEqual("Dimension Three");
782
+ expect(f.aspects().filter(a => a.isTaxonomyDefined())[0].value()).toEqual("eg:Member2");
783
+ expect(f.aspects().filter(a => a.isTaxonomyDefined())[0].valueLabel()).toEqual("Member Two");
784
+
785
+ expect(f.aspects().filter(a => a.isTaxonomyDefined()).at(-1).label()).toEqual("Axis One");
786
+ expect(f.aspects().filter(a => a.isTaxonomyDefined()).at(-1).valueLabel()).toEqual("Member Three");
787
+
788
+ expect(f.aspects().filter(a => a.isTaxonomyDefined()).map(k => k.name())).toEqual(["aa:Dimension3", "eg:Dimension1", "eg:Dimension2", "zz:Axis1"]);
789
+ expect(f.aspects().filter(a => !a.isTaxonomyDefined()).map(k => k.name())).toEqual(["c", "p", "u"]);
790
+ });
791
+ });
792
+
793
+ describe("Aspect sorting consistent between facts", () => {
794
+ var fact1 = testFact({
795
+ "d": -3,
796
+ "v": 1000,
797
+ "a": {
798
+ "c": "eg:Concept1",
799
+ "u": "iso4217:USD",
800
+ "p": "2018-01-01/2019-01-01",
801
+ "eg:Dimension2": "eg:Member1",
802
+ "aa:Dimension3": "eg:Member2",
803
+ "zz:Axis1": "eg:Member3",
804
+ "eg:Dimension1": "eg:Member4"
805
+ }});
806
+ var fact2 = testFact({
807
+ "d": -3,
808
+ "v": 1001,
809
+ "a": {
810
+ "c": "eg:Concept2",
811
+ "u": "iso4217:USD",
812
+ "p": "2018-01-01/2019-01-01",
813
+ "zz:Axis1": "eg:Member1",
814
+ "eg:Dimension2": "eg:Member2",
815
+ "aa:Dimension3": "eg:Member3",
816
+ "eg:Dimension1": "eg:Member4"
817
+ }});
818
+ test("fact1 dims are sorted as expected and in exactly the same order as fact2 dims", () => {
819
+ expect(fact1.aspects().filter(a => a.isTaxonomyDefined()).map(k => k.name())).toEqual(["aa:Dimension3", "eg:Dimension1", "eg:Dimension2", "zz:Axis1"]);
820
+ expect(fact1.aspects().filter(a => a.isTaxonomyDefined()).map(k => k.name())).toEqual(fact2.aspects().filter(a => a.isTaxonomyDefined()).map(k => k.name()));
821
+ });
822
+ });
823
+
824
+
693
825
  describe("Fact errors", () => {
694
826
  test("iXBRL Invalid", () => {
695
827
  var f = testFact({
@@ -707,7 +839,6 @@ describe("Fact errors", () => {
707
839
  expect(f.isMonetaryValue()).toBeTruthy();
708
840
  expect(f.readableValue()).toEqual("Invalid value");
709
841
  expect(f.unit().value()).toEqual("iso4217:USD");
710
- expect(f.measure()).toEqual("iso4217:USD");
711
842
  expect(f.conceptQName().prefix).toEqual("eg");
712
843
  expect(f.conceptQName().localname).toEqual("Concept1");
713
844
  expect(f.conceptQName().namespace).toEqual("http://www.example.com");
@@ -2,18 +2,31 @@
2
2
 
3
3
  import { Fact } from "./fact.js";
4
4
  import { Footnote } from "./footnote.js";
5
+ import { Interval } from './interval.js';
5
6
 
6
7
  export class FactSet {
7
- constructor(items) {
8
- this._items = items;
8
+
9
+ constructor (items) {
10
+ this.itemMap = new Map();
11
+ for (const item of items ?? []) {
12
+ this.add(item)
13
+ }
14
+ }
15
+
16
+ add(item) {
17
+ this.itemMap.set(item.vuid, item);
18
+ }
19
+
20
+ items() {
21
+ return Array.from(this.itemMap.values());
9
22
  }
10
23
 
11
24
  /* Returns the union of dimensions present on facts in the set */
12
25
  _allDimensions() {
13
- var dims = {};
14
- var facts = this._items.filter((item) => item instanceof Fact);
15
- for (var i = 0; i < facts.length; i++) {
16
- var dd = Object.keys(facts[i].dimensions());
26
+ const dims = {};
27
+ const facts = this.items().filter((item) => item instanceof Fact);
28
+ for (const fact of facts) {
29
+ const dd = Object.keys(fact.dimensions());
17
30
  for (var j = 0; j < dd.length; j++) {
18
31
  dims[dd[j]] = true;
19
32
  }
@@ -30,17 +43,17 @@ export class FactSet {
30
43
  */
31
44
  minimallyUniqueLabel(fact) {
32
45
  if (!this._minimallyUniqueLabels) {
33
- var facts = this._items.filter((item) => item instanceof Fact);
46
+ var facts = this.items().filter((item) => item instanceof Fact);
34
47
  var allLabels = {};
35
48
  var allAspects = ["c", "p"].concat(this._allDimensions());
36
49
  /* Assemble a map of arrays of all aspect labels for all facts, in a
37
50
  * consistent order */
38
51
  for (var i = 0; i < facts.length; i++) {
39
52
  var f = facts[i];
40
- allLabels[f.id] = [];
53
+ allLabels[f.vuid] = [];
41
54
  for (var j = 0; j < allAspects.length; j++) {
42
55
  var dd = f.aspect(allAspects[j]);
43
- allLabels[f.id].push(dd ? dd.valueLabel() : null);
56
+ allLabels[f.vuid].push(dd ? dd.valueLabel() : null);
44
57
  }
45
58
  }
46
59
  /* Iterate each aspect label and compare that label across all facts in
@@ -49,7 +62,7 @@ export class FactSet {
49
62
  for (var j = 0; j < allAspects.length; j++) {
50
63
  var labelMap = {};
51
64
  for (var i = 0; i < facts.length; i++) {
52
- labelMap[allLabels[facts[i].id][j]] = true;
65
+ labelMap[allLabels[facts[i].vuid][j]] = true;
53
66
  }
54
67
 
55
68
  var uniqueLabelsByLabel = {};
@@ -57,7 +70,7 @@ export class FactSet {
57
70
  /* We have at least two different labels, so include this
58
71
  * aspect in the label for all facts in the set */
59
72
  for (var i = 0; i < facts.length; i++) {
60
- var fid = facts[i].id;
73
+ var fid = facts[i].vuid;
61
74
  var l = allLabels[fid][j];
62
75
  var ul = uniqueLabels[fid] || [];
63
76
  if (l !== null) {
@@ -79,19 +92,55 @@ export class FactSet {
79
92
  * of them */
80
93
  if (Object.keys(uniqueLabels).length < facts.length) {
81
94
  for (var i = 0; i < facts.length; i++) {
82
- var fid = facts[i].id;
95
+ var fid = facts[i].vuid;
83
96
  var ul = uniqueLabels[fid] || [];
84
97
  ul.unshift(allLabels[fid][0]);
85
98
  uniqueLabels[fid] = ul;
86
99
  }
87
100
  }
88
101
 
89
- this._items.filter((item) => item instanceof Footnote).forEach((fn) => {
90
- uniqueLabels[fn.id] = [fn.title];
102
+ this.items().filter((item) => item instanceof Footnote).forEach((fn) => {
103
+ uniqueLabels[fn.vuid] = [fn.title];
91
104
  });
92
105
 
93
106
  this._minimallyUniqueLabels = uniqueLabels;
94
107
  }
95
- return this._minimallyUniqueLabels[fact.id].join(", ");
108
+ return this._minimallyUniqueLabels[fact.vuid].join(", ");
109
+ }
110
+
111
+ isEmpty() {
112
+ return this.itemMap.size == 0;
113
+ }
114
+
115
+ /*
116
+ * Returns an Interval for the intersection of all values in the set, or
117
+ * undefined if there is no intersection (inconsistent duplicates)
118
+ */
119
+ valueIntersection() {
120
+ const duplicates = this.items().map(fact => Interval.fromFact(fact));
121
+ return Interval.intersection(...duplicates);
122
+ }
123
+
124
+ completeDuplicates() {
125
+ return this.items().every(f => f.isCompleteDuplicate(this.items()[0]));
126
+ }
127
+
128
+ isConsistent() {
129
+ if (this.itemMap.size == 0) {
130
+ return true;
131
+ }
132
+ const duplicates = this.items().map(fact => Interval.fromFact(fact));
133
+ return Interval.intersection(...duplicates) !== undefined;
134
+ }
135
+
136
+ size() {
137
+ return this.itemMap.size;
138
+ }
139
+
140
+ /*
141
+ * Return the most precise (highest decimals) value within the set.
142
+ */
143
+ mostPrecise() {
144
+ return this.items().reduce((a, b) => b.isMorePrecise(a) ? b : a);
96
145
  }
97
146
  }