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.

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 +202 -131
  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 +27 -2
  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 +41 -8
  51. iXBRLViewerPlugin/viewer/src/js/fact.test.js +47 -13
  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 +22 -0
  69. iXBRLViewerPlugin/viewer/src/js/summary.test.js +50 -13
  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.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/METADATA +41 -14
  102. ixbrl_viewer-1.4.49.dist-info/RECORD +197 -0
  103. {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.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 +28 -0
  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.20.dist-info/RECORD +0 -166
  111. {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/LICENSE +0 -0
  112. {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/NOTICE +0 -0
  113. {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/entry_points.txt +0 -0
  114. {ixbrl_viewer-1.4.20.dist-info → ixbrl_viewer-1.4.49.dist-info}/top_level.txt +0 -0
@@ -39,7 +39,6 @@ const testReportData = {
39
39
  "roles": {},
40
40
  "roleDefs": {},
41
41
  "rels": {},
42
- "units": {},
43
42
  };
44
43
 
45
44
  function testReport(facts, ixData) {
@@ -159,7 +158,7 @@ describe("Scales filter options", () => {
159
158
  })
160
159
 
161
160
  test("Scales filter options with only monetary facts", () => {
162
- var insp = new TestInspector();
161
+ const insp = new TestInspector();
163
162
  const reportSet = testReport({
164
163
  ...monetaryFactData,
165
164
  }, ixData);
@@ -5,11 +5,17 @@ import $ from 'jquery'
5
5
  import { ReportSet } from "./reportset.js";
6
6
  import { Viewer, DocumentTooLargeError } from "./viewer.js";
7
7
  import { Inspector } from "./inspector.js";
8
+ import { initializeTheme } from './theme.js';
9
+ import { TaxonomyNamer } from './taxonomynamer.js';
10
+ import {FEATURE_GUIDE_LINK, FEATURE_REVIEW, FEATURE_SUPPORT_LINK, FEATURE_SURVEY_LINK} from "./util";
11
+
12
+ const featureFalsyValues = new Set([undefined, null, '', 'false', false]);
8
13
 
9
14
  export class iXBRLViewer {
10
15
 
11
16
  constructor(options) {
12
- this._features = new Set();
17
+ this._staticFeatures = {};
18
+ this._dynamicFeatures = {};
13
19
  this._plugins = [];
14
20
  this.inspector = new Inspector(this);
15
21
  this.viewer = null;
@@ -76,40 +82,68 @@ export class iXBRLViewer {
76
82
  });
77
83
  }
78
84
 
79
- setFeatures(featureNames, queryString) {
80
- const featureMap = {}
81
- // Enable given features initially
82
- featureNames.forEach(f => {
83
- featureMap[f] = true;
84
- });
85
+ setFeatures(features, queryString) {
86
+ this._staticFeatures = {}
87
+ for (const [key, value] of Object.entries(features)) {
88
+ this._staticFeatures[key] = value;
89
+ }
85
90
 
86
- // Enable/disable features based on query string
87
91
  const urlParams = new URLSearchParams(queryString);
92
+ this._dynamicFeatures = {}
88
93
  urlParams.forEach((value, key) => {
89
- // Do nothing if feature has already been disabled by query
90
- if (featureMap[key] !== false) {
91
- // Disable feature if value is exactly 'false', anything else enables the feature
92
- featureMap[key] = value !== 'false';
94
+ if (value === '') {
95
+ if (this._dynamicFeatures[key] === undefined) {
96
+ value = 'true'
97
+ } else {
98
+ return;
99
+ }
93
100
  }
101
+ this._dynamicFeatures[key] = value;
94
102
  });
103
+ }
95
104
 
96
- // Gather results in _features set
97
- if (this._features.size > 0) {
98
- this._features = new Set();
99
- }
100
- for (const [feature, enabled] of Object.entries(featureMap)) {
101
- if (enabled) {
102
- this._features.add(feature);
103
- }
105
+ getStaticFeatureValue(featureName) {
106
+ return this._staticFeatures[featureName];
107
+ }
108
+
109
+ getFeatureValue(featureName) {
110
+ if (this._dynamicFeatures[featureName]) {
111
+ return this._dynamicFeatures[featureName];
104
112
  }
113
+ return this.getStaticFeatureValue(featureName);
105
114
  }
106
115
 
107
116
  isFeatureEnabled(featureName) {
108
- return this._features.has(featureName);
117
+ return !featureFalsyValues.has(this.getFeatureValue(featureName));
118
+ }
119
+
120
+ isStaticFeatureEnabled(featureName) {
121
+ return !featureFalsyValues.has(this.getStaticFeatureValue(featureName));
109
122
  }
110
123
 
111
124
  isReviewModeEnabled() {
112
- return this.isFeatureEnabled('review');
125
+ return this.isFeatureEnabled(FEATURE_REVIEW);
126
+ }
127
+
128
+ getGuideLinkUrl() {
129
+ if (!this.isStaticFeatureEnabled(FEATURE_GUIDE_LINK)) {
130
+ return null;
131
+ }
132
+ return this.resolveRelativeUrl(this.getStaticFeatureValue(FEATURE_GUIDE_LINK));
133
+ }
134
+
135
+ getSupportLinkUrl() {
136
+ if (!this.isStaticFeatureEnabled(FEATURE_SUPPORT_LINK)) {
137
+ return null;
138
+ }
139
+ return this.resolveRelativeUrl(this.getStaticFeatureValue(FEATURE_SUPPORT_LINK));
140
+ }
141
+
142
+ getSurveyLinkUrl() {
143
+ if (!this.isStaticFeatureEnabled(FEATURE_SURVEY_LINK)) {
144
+ return null;
145
+ }
146
+ return this.resolveRelativeUrl(this.getStaticFeatureValue(FEATURE_SURVEY_LINK));
113
147
  }
114
148
 
115
149
  isViewerEnabled() {
@@ -117,16 +151,34 @@ export class iXBRLViewer {
117
151
  return (urlParams.get('disable-viewer') ?? 'false') === 'false';
118
152
  }
119
153
 
154
+ // Resolves URL relative to the config file
155
+ resolveRelativeUrl(url) {
156
+ if (this.options.configUrl === undefined) {
157
+ return url;
158
+ }
159
+ const resolvedUrl = new URL(url, this.options.configUrl.href);
160
+ return resolvedUrl.href;
161
+ }
162
+
120
163
  _loadInspectorHTML() {
121
164
  /* Insert HTML and CSS styles into body */
122
- $(require('../html/inspector.html')).prependTo('body');
123
- const inspector_css = require('css-loader!../less/inspector.less').toString();
165
+ const footerLogoHtml = this.runtimeConfig.skin?.footerLogoHtml ?? require("../html/footer-logo.html");
166
+ $(require('../html/inspector.html'))
167
+ .prependTo('body')
168
+ .find("#footer-logo").html(footerLogoHtml);
169
+ const inspector_css = require('../less/inspector.less').toString();
124
170
  $('<style id="ixv-style"></style>')
125
171
  .prop("type", "text/css")
126
172
  .text(inspector_css)
127
173
  .appendTo('head');
174
+ if (this.runtimeConfig.skin?.stylesheetUrl !== undefined) {
175
+ $('<link rel="stylesheet" id="ixv-style-skin" />')
176
+ .attr("href", this.resolveRelativeUrl(this.runtimeConfig.skin.stylesheetUrl))
177
+ .appendTo('head');
178
+ }
179
+ const favIconUrl = this.runtimeConfig.skin?.faviconUrl !== undefined ? this.resolveRelativeUrl(this.runtimeConfig.skin.faviconUrl) : require("../img/favicon.ico");
128
180
  $('<link id="ixv-favicon" type="image/x-icon" rel="shortcut icon" />')
129
- .attr('href', require('../img/favicon.ico'))
181
+ .attr('href', favIconUrl)
130
182
  .appendTo('head');
131
183
 
132
184
  try {
@@ -140,7 +192,7 @@ export class iXBRLViewer {
140
192
  _reparentDocument() {
141
193
  const iframeContainer = $('#ixv #iframe-container');
142
194
 
143
- const iframe = $('<iframe title="iXBRL document view"/>')
195
+ const iframe = $('<iframe title="iXBRL document view" tabindex="0"/>')
144
196
  .data("report-index", 0)
145
197
  .appendTo(iframeContainer)[0];
146
198
 
@@ -160,7 +212,8 @@ export class iXBRLViewer {
160
212
  $('html').attr("lang", "en-US");
161
213
  }
162
214
 
163
- $('head').children().not("script").not("style#ixv-style").not("link#ixv-favicon").appendTo($(iframe).contents().find('head'));
215
+ $('head')
216
+ .children().not("script, style#ixv-style, link#ixv-style-skin, link#ixv-favicon").appendTo($(iframe).contents().find('head'));
164
217
 
165
218
  $('<title>').text(docTitle).appendTo($('head'));
166
219
 
@@ -197,11 +250,41 @@ export class iXBRLViewer {
197
250
  }
198
251
  }
199
252
 
253
+ _loadRuntimeConfig() {
254
+ return new Promise((resolve, reject) => {
255
+ if (this.options.configUrl === undefined) {
256
+ resolve({});
257
+ }
258
+ else {
259
+ fetch(this.options.configUrl)
260
+ .then((resp) => {
261
+ if (resp.status == 404) {
262
+ return Promise.resolve({});
263
+ }
264
+ else if (resp.status != 200) {
265
+ return Promise.reject("Fetch failed: " + resp.status);
266
+ }
267
+ return resp.json();
268
+ })
269
+ .then((data) => {
270
+ resolve(data);
271
+ })
272
+ .catch((err) => {
273
+ console.log(err);
274
+ resolve({});
275
+ });
276
+ }
277
+ });
278
+ }
279
+
200
280
  load() {
201
281
  const iv = this;
202
282
  const inspector = this.inspector;
283
+
284
+ this._loadRuntimeConfig().then((runtimeConfig) => {
285
+ this.runtimeConfig = runtimeConfig;
286
+ initializeTheme();
203
287
 
204
- setTimeout(function () {
205
288
  const stubViewer = $('body').hasClass('ixv-stub-viewer');
206
289
 
207
290
  // If viewer is disabled, but not in stub viewer mode, just abort
@@ -217,9 +300,25 @@ export class iXBRLViewer {
217
300
  // We need to parse JSON first so that we can determine feature enablement before loading begins.
218
301
  const taxonomyData = iv._getTaxonomyData();
219
302
  const parsedTaxonomyData = taxonomyData && JSON.parse(taxonomyData);
220
- iv.setFeatures((parsedTaxonomyData && parsedTaxonomyData["features"]) || [], window.location.search);
303
+ let features = parsedTaxonomyData && parsedTaxonomyData["features"];
304
+ if (!features) {
305
+ features = {};
306
+ }
307
+ // `features` was previously an array of flag values
308
+ // Support this for backwards compatability
309
+ else if (Array.isArray(features)) {
310
+ features = features.reduce((obj, val) => {
311
+ obj[val] = true;
312
+ return obj;
313
+ }, {});
314
+ }
315
+ if (this.runtimeConfig.features !== undefined) {
316
+ features = {...this.runtimeConfig.features, features};
317
+ }
318
+ iv.setFeatures(features, window.location.search);
221
319
 
222
320
  const reportSet = new ReportSet(parsedTaxonomyData);
321
+ reportSet.taxonomyNamer = new TaxonomyNamer(new Map(Object.entries(this.runtimeConfig.taxonomyNames ?? {})));
223
322
 
224
323
  // Viewer disabled in stub viewer mode => redirect to first iXBRL document
225
324
  if (!iv.isViewerEnabled()) {
@@ -239,7 +338,7 @@ export class iXBRLViewer {
239
338
  const ds = reportSet.reportFiles();
240
339
  let hasExternalIframe = false;
241
340
  for (let i = stubViewer ? 0 : 1; i < ds.length; i++) {
242
- const iframe = $("<iframe />").attr("src", ds[i].file).data("report-index", ds[i].index).appendTo("#ixv #iframe-container");
341
+ const iframe = $('<iframe tabindex="0" />').attr("src", ds[i].file).data("report-index", ds[i].index).appendTo("#ixv #iframe-container");
243
342
  iframes = iframes.add(iframe);
244
343
  hasExternalIframe = true;
245
344
  }
@@ -292,7 +391,6 @@ export class iXBRLViewer {
292
391
  $('#ixv .loader').remove();
293
392
 
294
393
  /* Focus on fact specified in URL fragment, if any */
295
- inspector.handleFactDeepLink();
296
394
  if (iv.options.showValidationWarningOnStart) {
297
395
  inspector.showValidationWarning();
298
396
  }
@@ -1,6 +1,12 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
3
  import {iXBRLViewer} from "./ixbrlviewer";
4
+ import {
5
+ FEATURE_GUIDE_LINK,
6
+ FEATURE_REVIEW,
7
+ FEATURE_SUPPORT_LINK,
8
+ FEATURE_SURVEY_LINK
9
+ } from "./util";
4
10
  describe("Feature enablement", () => {
5
11
  var viewer = null;
6
12
  beforeAll(() => {
@@ -8,58 +14,99 @@ describe("Feature enablement", () => {
8
14
  });
9
15
 
10
16
  test("Query parameter with no value results in enablement", () => {
11
- viewer.setFeatures([], 'a')
12
- expect(viewer._features).toEqual(new Set(['a']));
17
+ viewer.setFeatures({}, 'a')
18
+ expect(viewer._staticFeatures).toEqual({});
19
+ expect(viewer._dynamicFeatures).toEqual({'a': 'true'});
13
20
  expect(viewer.isFeatureEnabled('a')).toBeTruthy();
14
21
  });
15
22
 
16
23
  test("Excluded from JSON, excluded from query", () => {
17
- viewer.setFeatures([], '')
18
- expect(viewer._features).toEqual(new Set([]));
24
+ viewer.setFeatures({}, '')
25
+ expect(viewer._staticFeatures).toEqual({});
26
+ expect(viewer._dynamicFeatures).toEqual({});
19
27
  expect(viewer.isFeatureEnabled('a')).toBeFalsy();
20
28
  });
21
29
 
22
30
  test("Excluded from JSON, enabled in query", () => {
23
- viewer.setFeatures([], 'a=true')
24
- expect(viewer._features).toEqual(new Set(['a']));
31
+ viewer.setFeatures({}, 'a=true')
32
+ expect(viewer._staticFeatures).toEqual({});
33
+ expect(viewer._dynamicFeatures).toEqual({'a': 'true'});
25
34
  expect(viewer.isFeatureEnabled('a')).toBeTruthy();
26
35
  });
27
36
 
28
37
  test("Excluded from JSON, disabled in query", () => {
29
- viewer.setFeatures([], 'a=false')
30
- expect(viewer._features).toEqual(new Set([]));
38
+ viewer.setFeatures({}, 'a=false')
39
+ expect(viewer._staticFeatures).toEqual({});
40
+ expect(viewer._dynamicFeatures).toEqual({'a': 'false'});
31
41
  expect(viewer.isFeatureEnabled('a')).toBeFalsy();
32
42
  });
33
43
 
34
44
  test("Excluded from JSON, enabled and disabled in query", () => {
35
- viewer.setFeatures([], 'a=true&a=false&a')
36
- expect(viewer._features).toEqual(new Set([]));
45
+ viewer.setFeatures({}, 'a=true&a=false&a')
46
+ expect(viewer._staticFeatures).toEqual({});
47
+ expect(viewer._dynamicFeatures).toEqual({'a': 'false'});
37
48
  expect(viewer.isFeatureEnabled('a')).toBeFalsy();
38
49
  });
39
50
 
40
51
  test("Included in JSON, excluded from query", () => {
41
- viewer.setFeatures(['a'], '')
42
- expect(viewer._features).toEqual(new Set(['a']));
52
+ viewer.setFeatures({'a': true}, '')
53
+ expect(viewer._staticFeatures).toEqual({'a': true});
54
+ expect(viewer._dynamicFeatures).toEqual({});
43
55
  expect(viewer.isFeatureEnabled('a')).toBeTruthy();
44
56
  });
45
57
 
46
58
  test("Included in JSON, enabled in query", () => {
47
- viewer.setFeatures(['a'], 'a=true')
48
- expect(viewer._features).toEqual(new Set(['a']));
59
+ viewer.setFeatures({'a': true}, 'a=true')
60
+ expect(viewer._staticFeatures).toEqual({'a': true});
61
+ expect(viewer._dynamicFeatures).toEqual({'a': 'true'});
49
62
  expect(viewer.isFeatureEnabled('a')).toBeTruthy();
50
63
  });
51
64
 
52
65
  test("Included in JSON, disabled in query", () => {
53
- viewer.setFeatures(['a'], 'a=false')
54
- expect(viewer._features).toEqual(new Set([]));
66
+ viewer.setFeatures({'a': true}, 'a=false')
67
+ expect(viewer._staticFeatures).toEqual({'a': true});
68
+ expect(viewer._dynamicFeatures).toEqual({'a': 'false'});
55
69
  expect(viewer.isFeatureEnabled('a')).toBeFalsy();
56
70
  });
57
71
 
58
72
  test("Included in JSON, enabled and disabled in query", () => {
59
- viewer.setFeatures(['a'], 'a=true&a=false&a')
60
- expect(viewer._features).toEqual(new Set([]));
73
+ viewer.setFeatures({'a': true}, 'a=true&a=false&a')
74
+ expect(viewer._staticFeatures).toEqual({'a': true});
75
+ expect(viewer._dynamicFeatures).toEqual({'a': 'false'});
61
76
  expect(viewer.isFeatureEnabled('a')).toBeFalsy();
62
77
  });
78
+
79
+ test("Value set in JSON, not included in query", () => {
80
+ viewer.setFeatures({'a': '1'}, '')
81
+ expect(viewer._staticFeatures).toEqual({'a': '1'});
82
+ expect(viewer._dynamicFeatures).toEqual({});
83
+ expect(viewer.getFeatureValue('a')).toEqual('1');
84
+ expect(viewer.isFeatureEnabled('a')).toBeTruthy();
85
+ });
86
+
87
+ test("Value not set in JSON, set in query", () => {
88
+ viewer.setFeatures({}, 'a=1')
89
+ expect(viewer._staticFeatures).toEqual({});
90
+ expect(viewer._dynamicFeatures).toEqual({'a': '1'});
91
+ expect(viewer.getFeatureValue('a')).toEqual('1');
92
+ expect(viewer.isFeatureEnabled('a')).toBeTruthy();
93
+ });
94
+
95
+ test("Value set in JSON, overwritten in query", () => {
96
+ viewer.setFeatures({'a': '1'}, 'a=2')
97
+ expect(viewer._staticFeatures).toEqual({'a': '1'});
98
+ expect(viewer._dynamicFeatures).toEqual({'a': '2'});
99
+ expect(viewer.getFeatureValue('a')).toEqual('2');
100
+ expect(viewer.isFeatureEnabled('a')).toBeTruthy();
101
+ });
102
+
103
+ test("Value set in JSON, overwritten in query with blank", () => {
104
+ viewer.setFeatures({'a': '1'}, 'a')
105
+ expect(viewer._staticFeatures).toEqual({'a': '1'});
106
+ expect(viewer._dynamicFeatures).toEqual({'a': 'true'});
107
+ expect(viewer.getFeatureValue('a')).toEqual('true');
108
+ expect(viewer.isFeatureEnabled('a')).toBeTruthy();
109
+ });
63
110
  });
64
111
 
65
112
  describe("Review mode enablement", () => {
@@ -69,12 +116,78 @@ describe("Review mode enablement", () => {
69
116
  });
70
117
 
71
118
  test("Review mode enabled", () => {
72
- viewer.setFeatures(['review'], '')
119
+ viewer.setFeatures({[FEATURE_REVIEW]: true}, '')
73
120
  expect(viewer.isReviewModeEnabled()).toBeTruthy();
74
121
  });
75
122
 
76
123
  test("Review mode disabled", () => {
77
- viewer.setFeatures(['a'], '')
124
+ viewer.setFeatures({'a': true}, '')
78
125
  expect(viewer.isReviewModeEnabled()).toBeFalsy();
79
126
  });
80
127
  });
128
+
129
+ describe("Support link enablement", () => {
130
+ var viewer = null;
131
+ beforeAll(() => {
132
+ viewer = new iXBRLViewer({})
133
+ });
134
+
135
+ test("Support link enabled by JSON", () => {
136
+ viewer.setFeatures({[FEATURE_SUPPORT_LINK]: '/help'}, '')
137
+ expect(viewer.getSupportLinkUrl()).toEqual('/help');
138
+ });
139
+
140
+ test("Support link enabled by query", () => {
141
+ viewer.setFeatures({}, FEATURE_SUPPORT_LINK + '=/help')
142
+ expect(viewer.getSupportLinkUrl()).toEqual(null);
143
+ });
144
+
145
+ test("Support link disabled by query", () => {
146
+ viewer.setFeatures({[FEATURE_SUPPORT_LINK]: '/help'}, FEATURE_SUPPORT_LINK + '=false')
147
+ expect(viewer.getSupportLinkUrl()).toEqual('/help');
148
+ });
149
+ });
150
+
151
+ describe("Survey link enablement", () => {
152
+ var viewer = null;
153
+ beforeAll(() => {
154
+ viewer = new iXBRLViewer({})
155
+ });
156
+
157
+ test("Survey link enabled by JSON", () => {
158
+ viewer.setFeatures({[FEATURE_SURVEY_LINK]: '/survey'}, '')
159
+ expect(viewer.getSurveyLinkUrl()).toEqual('/survey');
160
+ });
161
+
162
+ test("Survey link enabled by query", () => {
163
+ viewer.setFeatures({}, FEATURE_SURVEY_LINK + '=/survey')
164
+ expect(viewer.getSurveyLinkUrl()).toEqual(null);
165
+ });
166
+
167
+ test("Survey link disabled by query", () => {
168
+ viewer.setFeatures({[FEATURE_SURVEY_LINK]: '/survey'}, FEATURE_SURVEY_LINK + '=false')
169
+ expect(viewer.getSurveyLinkUrl()).toEqual('/survey');
170
+ });
171
+ });
172
+
173
+ describe("Guide link enablement", () => {
174
+ var viewer = null;
175
+ beforeAll(() => {
176
+ viewer = new iXBRLViewer({})
177
+ });
178
+
179
+ test("Guide link enabled by JSON", () => {
180
+ viewer.setFeatures({[FEATURE_GUIDE_LINK]: '/guide'}, '')
181
+ expect(viewer.getGuideLinkUrl()).toEqual('/guide');
182
+ });
183
+
184
+ test("Guide link enabled by query", () => {
185
+ viewer.setFeatures({}, FEATURE_GUIDE_LINK + '=/guide')
186
+ expect(viewer.getGuideLinkUrl()).toEqual(null);
187
+ });
188
+
189
+ test("Guide link disabled by query", () => {
190
+ viewer.setFeatures({[FEATURE_GUIDE_LINK]: '/guide'}, FEATURE_GUIDE_LINK + '=false')
191
+ expect(viewer.getGuideLinkUrl()).toEqual('/guide');
192
+ });
193
+ });
@@ -46,6 +46,6 @@ export class IXNode {
46
46
  }
47
47
 
48
48
  htmlHidden() {
49
- return this.wrapperNodes.is(':hidden') || this.wrapperNodes.is((i,e) => isTransparent($(e).css('color')));
49
+ return this.wrapperNodes.filter(':not(.ixbrl-no-highlight)').is(':hidden') || this.wrapperNodes.is((i,e) => isTransparent($(e).css('color')));
50
50
  }
51
51
  }
@@ -1,6 +1,7 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
3
  import $ from 'jquery'
4
+ import i18next from "i18next";
4
5
 
5
6
  export class Menu {
6
7
  constructor(elt, attr) {
@@ -8,14 +9,14 @@ export class Menu {
8
9
  attr = attr || {};
9
10
  this.type = attr.type || "dropdown";
10
11
 
11
- elt.find(".menu-title").click((e) => {
12
+ elt.find(".menu-title").on("click", (e) => {
12
13
  elt.find(".content-container").toggle();
13
14
  /* Stop an opening click from also being treated as an "out-of-menu"
14
15
  * closing click */
15
16
  e.stopPropagation();
16
17
  });
17
18
 
18
- $('html').click((event) => {
19
+ $('html').on("click", (event) => {
19
20
  if ($(".content", elt).find($(event.target)).length === 0) {
20
21
  this.close();
21
22
  }
@@ -50,7 +51,7 @@ export class Menu {
50
51
  .attr({
51
52
  href: filename})
52
53
  .text(name)
53
- .click(() => menu.close());
54
+ .on("click", () => menu.close());
54
55
  this._add(item);
55
56
  }
56
57
 
@@ -65,14 +66,14 @@ export class Menu {
65
66
  $('<input type="checkbox"></input>')
66
67
  .prop("checked", onByDefault)
67
68
  .change(function () {
68
- callback($(this).prop("checked"));
69
+ callback($(this).prop("checked"), true);
69
70
  menu.close();
70
71
  })
71
72
  )
72
73
  .append($("<span></span>").addClass("checkmark"));
73
74
  this._add(item, after);
74
75
  if (onByDefault) {
75
- callback(true);
76
+ callback(true, false);
76
77
  }
77
78
  }
78
79
 
@@ -89,9 +90,8 @@ export class Menu {
89
90
  .prepend(
90
91
  $('<input type="radio"></input>')
91
92
  .attr({ "name": name, "value": v})
92
- .change(function () {
93
+ .on("change", function () {
93
94
  callback($(this).val())
94
- menu.close();
95
95
  })
96
96
  )
97
97
  .append($("<span></span>").addClass("checkmark"))
@@ -102,4 +102,22 @@ export class Menu {
102
102
  }
103
103
  }
104
104
  }
105
+
106
+ addLabel(name) {
107
+ const item = $('<div></div>')
108
+ .addClass("label")
109
+ .text(name)
110
+ this._add(item);
111
+ }
112
+
113
+ addLink(label, href) {
114
+ const item = $("<a></a>")
115
+ .attr("href", href)
116
+ .attr("target", "_blank")
117
+ .attr("title", label)
118
+ .attr("aria-label", label)
119
+ .addClass("item")
120
+ .text(label);
121
+ this._add(item);
122
+ }
105
123
  }
@@ -47,9 +47,9 @@ const hyphen = '[-\u2014]';
47
47
 
48
48
  // everything that we allow immediately before a match
49
49
  // (201c = left double quote, 2014 = em dash, 201d = right double quote)
50
- const begin_guard = '(?:^|(?<=(?:[/\u201c\u2014(]|' + one_ws + '|' + hyphen + ')))';
50
+ const begin_guard = '(?:^|(?<=(?:[/\u201c\u2014([]|' + one_ws + '|' + hyphen + ')))';
51
51
  // everything that we allow immediately after a match
52
- const end_guard = '(?:$|(?=(?:[/,.:;\u201d\u2014)]|' + one_ws + '|' + hyphen + ')))';
52
+ const end_guard = '(?:$|(?=(?:[/,.:;\u201d\u2014)\\]]|' + one_ws + '|' + hyphen + ')))';
53
53
 
54
54
  const ordinal_suffix = '(?:st|nd|rd|th)';
55
55
  const day = '[123]?\\d' + ordinal_suffix + '?';
@@ -1,7 +1,5 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
- import $ from 'jquery'
4
-
5
3
  export class ReportSetOutline {
6
4
  constructor(reportSet) {
7
5
  this._reportSet = reportSet;
@@ -173,9 +171,9 @@ export class DocumentOutline {
173
171
  sortedSections() {
174
172
  const sections = Object.keys(this.sections);
175
173
  const re = /\(parenthetical\)\s*$/i;
176
- const filteredSections = sections.filter(s => !re.test(this.report.getRoleLabel(s)));
174
+ const filteredSections = sections.filter(s => !re.test(this.report.getRoleLabelOrURI(s)));
177
175
  return filteredSections
178
- .sort((a, b) => this.report.getRoleLabel(a).localeCompare(this.report.getRoleLabel(b)))
176
+ .sort((a, b) => this.report.getRoleLabelOrURI(a).localeCompare(this.report.getRoleLabelOrURI(b)))
179
177
  .map(elr => ({ report: this.report, fact: this.sections[elr], elr: elr }));
180
178
  }
181
179
  }
@@ -1,7 +1,6 @@
1
1
  // See COPYRIGHT.md for copyright information
2
2
 
3
3
  import { xbrlDateToMoment, momentToHuman } from "./util.js"
4
- import moment from "moment";
5
4
 
6
5
  export class Period {
7
6
  constructor(p) {